home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / hity wydania / Ubuntu 9.10 PL / karmelkowy-koliberek-desktop-9.10-i386-PL.iso / casper / filesystem.squashfs / usr / share / ubiquity / install.py < prev    next >
Text File  |  2009-10-28  |  97KB  |  2,448 lines

  1. #!/usr/bin/python
  2. # -*- coding: utf-8; Mode: Python; indent-tabs-mode: nil; tab-width: 4 -*-
  3.  
  4. # Copyright (C) 2005 Javier Carranza and others for Guadalinex
  5. # Copyright (C) 2005, 2006, 2007, 2008, 2009 Canonical Ltd.
  6. # Copyright (C) 2007 Mario Limonciello
  7. #
  8. # This program is free software; you can redistribute it and/or modify
  9. # it under the terms of the GNU General Public License as published by
  10. # the Free Software Foundation; either version 2 of the License, or
  11. # (at your option) any later version.
  12. #
  13. # This program is distributed in the hope that it will be useful,
  14. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16. # GNU General Public License for more details.
  17. #
  18. # You should have received a copy of the GNU General Public License
  19. # along with this program; if not, write to the Free Software
  20. # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  21.  
  22. import sys
  23. import os
  24. import platform
  25. import errno
  26. import stat
  27. import re
  28. import textwrap
  29. import shutil
  30. import subprocess
  31. import time
  32. import struct
  33. import socket
  34. import fcntl
  35. import traceback
  36. import syslog
  37. import gzip
  38. import debconf
  39. import warnings
  40. warnings.filterwarnings("ignore", "apt API not stable yet", FutureWarning)
  41. import apt_pkg
  42. from apt.cache import Cache
  43. from apt.progress import FetchProgress, InstallProgress
  44. from hashlib import md5
  45.  
  46. sys.path.insert(0, '/usr/lib/ubiquity')
  47.  
  48. from ubiquity import misc
  49. from ubiquity import osextras
  50. from ubiquity import plugin_manager
  51. from ubiquity.casper import get_casper
  52. from ubiquity.components import apt_setup, usersetup_apply, \
  53.                                 hw_detect, check_kernels, \
  54.                                 migrationassistant_apply
  55.  
  56. def debconf_disconnect():
  57.     """Disconnect from debconf. This is only to be used as a subprocess
  58.     preexec_fn helper."""
  59.     os.environ['DEBIAN_FRONTEND'] = 'noninteractive'
  60.     if 'DEBIAN_HAS_FRONTEND' in os.environ:
  61.         del os.environ['DEBIAN_HAS_FRONTEND']
  62.     if 'DEBCONF_USE_CDEBCONF' in os.environ:
  63.         # Probably not a good idea to use this in /target too ...
  64.         del os.environ['DEBCONF_USE_CDEBCONF']
  65.  
  66. class DebconfFetchProgress(FetchProgress):
  67.     """An object that reports apt's fetching progress using debconf."""
  68.  
  69.     def __init__(self, db, title, info_starting, info):
  70.         FetchProgress.__init__(self)
  71.         self.db = db
  72.         self.title = title
  73.         self.info_starting = info_starting
  74.         self.info = info
  75.         self.old_capb = None
  76.         self.eta = 0.0
  77.  
  78.     def start(self):
  79.         self.db.progress('START', 0, 100, self.title)
  80.         if self.info_starting is not None:
  81.             self.db.progress('INFO', self.info_starting)
  82.         self.old_capb = self.db.capb()
  83.         capb_list = self.old_capb.split()
  84.         capb_list.append('progresscancel')
  85.         self.db.capb(' '.join(capb_list))
  86.  
  87.     # TODO cjwatson 2006-02-27: implement updateStatus
  88.  
  89.     def pulse(self):
  90.         FetchProgress.pulse(self)
  91.         try:
  92.             self.db.progress('SET', int(self.percent))
  93.         except debconf.DebconfError:
  94.             return False
  95.         if self.eta != 0.0:
  96.             time_str = "%d:%02d" % divmod(int(self.eta), 60)
  97.             self.db.subst(self.info, 'TIME', time_str)
  98.             try:
  99.                 self.db.progress('INFO', self.info)
  100.             except debconf.DebconfError:
  101.                 return False
  102.         return True
  103.  
  104.     def stop(self):
  105.         if self.old_capb is not None:
  106.             self.db.capb(self.old_capb)
  107.             self.old_capb = None
  108.             self.db.progress('STOP')
  109.  
  110. class DebconfInstallProgress(InstallProgress):
  111.     """An object that reports apt's installation progress using debconf."""
  112.  
  113.     def __init__(self, db, title, info, error=None):
  114.         InstallProgress.__init__(self)
  115.         self.db = db
  116.         self.title = title
  117.         self.info = info
  118.         self.error_template = error
  119.         self.started = False
  120.         # InstallProgress uses a non-blocking status fd; our run()
  121.         # implementation doesn't need that, and in fact we spin unless the
  122.         # fd is blocking.
  123.         flags = fcntl.fcntl(self.statusfd.fileno(), fcntl.F_GETFL)
  124.         fcntl.fcntl(self.statusfd.fileno(), fcntl.F_SETFL,
  125.                     flags & ~os.O_NONBLOCK)
  126.  
  127.     def startUpdate(self):
  128.         self.db.progress('START', 0, 100, self.title)
  129.         self.started = True
  130.  
  131.     def error(self, pkg, errormsg):
  132.         if self.error_template is not None:
  133.             self.db.subst(self.error_template, 'PACKAGE', pkg)
  134.             self.db.subst(self.error_template, 'MESSAGE', errormsg)
  135.             self.db.input('critical', self.error_template)
  136.             self.db.go()
  137.  
  138.     def statusChange(self, pkg, percent, status):
  139.         self.percent = percent
  140.         self.status = status
  141.         self.db.progress('SET', int(percent))
  142.         self.db.subst(self.info, 'DESCRIPTION', status)
  143.         self.db.progress('INFO', self.info)
  144.  
  145.     def updateInterface(self):
  146.         # TODO cjwatson 2006-02-28: InstallProgress.updateInterface doesn't
  147.         # give us a handy way to spot when percentages/statuses change and
  148.         # aren't pmerror/pmconffile, so we have to reimplement it here.
  149.         if self.statusfd is None:
  150.             return False
  151.         try:
  152.             while not self.read.endswith("\n"):
  153.                 r = os.read(self.statusfd.fileno(),1)
  154.                 if not r:
  155.                     return False
  156.                 self.read += r
  157.         except OSError, (err,errstr):
  158.             print errstr
  159.         if self.read.endswith("\n"):
  160.             s = self.read
  161.             (status, pkg, percent, status_str) = s.split(":", 3)
  162.             if status == "pmerror":
  163.                 self.error(pkg, status_str)
  164.             elif status == "pmconffile":
  165.                 # we get a string like this:
  166.                 # 'current-conffile' 'new-conffile' useredited distedited
  167.                 match = re.compile("\s*\'(.*)\'\s*\'(.*)\'.*").match(status_str)
  168.                 if match:
  169.                     self.conffile(match.group(1), match.group(2))
  170.             else:
  171.                 self.statusChange(pkg, float(percent), status_str.strip())
  172.             self.read = ""
  173.         return True
  174.  
  175.     def run(self, pm):
  176.         # Create a subprocess to deal with turning apt status messages into
  177.         # debconf protocol messages.
  178.         child_pid = self.fork()
  179.         if child_pid == 0:
  180.             # child
  181.             os.close(self.writefd)
  182.             try:
  183.                 while self.updateInterface():
  184.                     pass
  185.             except (KeyboardInterrupt, SystemExit):
  186.                 pass # we're going to exit anyway
  187.             except:
  188.                 for line in traceback.format_exc().split('\n'):
  189.                     syslog.syslog(syslog.LOG_WARNING, line)
  190.             os._exit(0)
  191.  
  192.         self.statusfd.close()
  193.  
  194.         # Redirect stdin from /dev/null and stdout to stderr to avoid them
  195.         # interfering with our debconf protocol stream.
  196.         saved_stdin = os.dup(0)
  197.         try:
  198.             null = os.open('/dev/null', os.O_RDONLY)
  199.             os.dup2(null, 0)
  200.             os.close(null)
  201.         except OSError:
  202.             pass
  203.         saved_stdout = os.dup(1)
  204.         os.dup2(2, 1)
  205.  
  206.         # Make sure all packages are installed non-interactively. We
  207.         # don't have enough passthrough magic here to deal with any
  208.         # debconf questions they might ask.
  209.         saved_environ_keys = ('DEBIAN_FRONTEND', 'DEBIAN_HAS_FRONTEND',
  210.                               'DEBCONF_USE_CDEBCONF')
  211.         saved_environ = {}
  212.         for key in saved_environ_keys:
  213.             if key in os.environ:
  214.                 saved_environ[key] = os.environ[key]
  215.         os.environ['DEBIAN_FRONTEND'] = 'noninteractive'
  216.         if 'DEBIAN_HAS_FRONTEND' in os.environ:
  217.             del os.environ['DEBIAN_HAS_FRONTEND']
  218.         if 'DEBCONF_USE_CDEBCONF' in os.environ:
  219.             # Probably not a good idea to use this in /target too ...
  220.             del os.environ['DEBCONF_USE_CDEBCONF']
  221.  
  222.         res = pm.ResultFailed
  223.         try:
  224.             res = pm.DoInstall(self.writefd)
  225.         finally:
  226.             # Reap the status-to-debconf subprocess.
  227.             os.close(self.writefd)
  228.             while True:
  229.                 try:
  230.                     (pid, status) = os.waitpid(child_pid, 0)
  231.                     if pid != child_pid:
  232.                         break
  233.                     if os.WIFEXITED(status) or os.WIFSIGNALED(status):
  234.                         break
  235.                 except OSError:
  236.                     break
  237.  
  238.             # Put back stdin and stdout.
  239.             os.dup2(saved_stdin, 0)
  240.             os.close(saved_stdin)
  241.             os.dup2(saved_stdout, 1)
  242.             os.close(saved_stdout)
  243.  
  244.             # Put back the environment.
  245.             for key in saved_environ_keys:
  246.                 if key in saved_environ:
  247.                     os.environ[key] = saved_environ[key]
  248.                 elif key in os.environ:
  249.                     del os.environ[key]
  250.  
  251.         return res
  252.  
  253.     def finishUpdate(self):
  254.         if self.started:
  255.             self.db.progress('STOP')
  256.             self.started = False
  257.  
  258. class InstallStepError(Exception):
  259.     """Raised when an install step fails."""
  260.  
  261.     def __init__(self, message):
  262.         Exception.__init__(self, message)
  263.  
  264. class Install:
  265.  
  266.     def __init__(self):
  267.         """Initial attributes."""
  268.  
  269.         if os.path.isdir('/rofs'):
  270.             self.source = '/rofs'
  271.         elif os.path.isdir('/UNIONFS'):
  272.             # Klaus Knopper says this may not actually work very well
  273.             # because it'll copy the WHOLE WORLD (~12GB).
  274.             self.source = '/UNIONFS'
  275.         else:
  276.             self.source = '/var/lib/ubiquity/source'
  277.         self.target = '/target'
  278.         self.casper_path = os.path.join(
  279.             '/cdrom', get_casper('LIVE_MEDIA_PATH', 'casper').lstrip('/'))
  280.         self.kernel_version = platform.release()
  281.         self.db = debconf.Debconf()
  282.         self.languages = []
  283.         self.langpacks = []
  284.         self.blacklist = {}
  285.  
  286.         # Load plugins
  287.         modules = plugin_manager.load_plugins()
  288.         modules = plugin_manager.order_plugins(modules)
  289.         self.plugins = [x for x in modules if hasattr(x, 'Install')]
  290.  
  291.         if 'UBIQUITY_OEM_USER_CONFIG' in os.environ:
  292.             self.source = None
  293.             self.target = '/'
  294.             return
  295.  
  296.         self.select_language_packs()
  297.         self.select_ecryptfs()
  298.         use_restricted = True
  299.         try:
  300.             if self.db.get('apt-setup/restricted') == 'false':
  301.                 use_restricted = False
  302.         except debconf.DebconfError:
  303.             pass
  304.         if not use_restricted:
  305.             self.restricted_cache = Cache()
  306.         if self.db.get('ubiquity/install/generate-blacklist') == 'true':
  307.             self.db.progress('START', 0, 100, 'ubiquity/install/title')
  308.             self.db.progress('INFO', 'ubiquity/install/blacklist')
  309.             self.generate_blacklist()
  310.  
  311.         apt_pkg.InitConfig()
  312.         apt_pkg.Config.Set("Dir", self.target)
  313.         apt_pkg.Config.Set("Dir::State::status",
  314.                            os.path.join(self.target, 'var/lib/dpkg/status'))
  315.         apt_pkg.Config.Set("APT::GPGV::TrustedKeyring",
  316.                            os.path.join(self.target, 'etc/apt/trusted.gpg'))
  317.         apt_pkg.Config.Set("Acquire::gpgv::Options::",
  318.                            "--ignore-time-conflict")
  319.         apt_pkg.Config.Set("DPkg::Options::", "--root=%s" % self.target)
  320.         # We don't want apt-listchanges or dpkg-preconfigure, so just clear
  321.         # out the list of pre-installation hooks.
  322.         apt_pkg.Config.Clear("DPkg::Pre-Install-Pkgs")
  323.         apt_pkg.InitSystem()
  324.  
  325.     def excepthook(self, exctype, excvalue, exctb):
  326.         """Crash handler. Dump the traceback to a file so that it can be
  327.         read by the caller."""
  328.  
  329.         if (issubclass(exctype, KeyboardInterrupt) or
  330.             issubclass(exctype, SystemExit)):
  331.             return
  332.  
  333.         tbtext = ''.join(traceback.format_exception(exctype, excvalue, exctb))
  334.         syslog.syslog(syslog.LOG_ERR, "Exception during installation:")
  335.         for line in tbtext.split('\n'):
  336.             syslog.syslog(syslog.LOG_ERR, line)
  337.         tbfile = open('/var/lib/ubiquity/install.trace', 'w')
  338.         print >>tbfile, tbtext
  339.         tbfile.close()
  340.  
  341.         sys.exit(1)
  342.  
  343.     def run(self):
  344.         """Run the install stage: copy everything to the target system, then
  345.         configure it as necessary."""
  346.  
  347.         # Give one extra progress point for each plugin, on the assumption that
  348.         # they don't run long.
  349.         start = 0
  350.         end = 22 + len(self.plugins)
  351.         if self.target != '/':
  352.             end += 74
  353.         count = 1
  354.  
  355.         self.db.progress('START', start, end, 'ubiquity/install/title')
  356.         self.db.progress('INFO', 'ubiquity/install/mounting_source')
  357.  
  358.         try:
  359.             if self.source == '/var/lib/ubiquity/source':
  360.                 self.mount_source()
  361.  
  362.             if self.target != '/':
  363.                 self.db.progress('SET', count)
  364.                 self.db.progress('REGION', count, count+74)
  365.                 count += 74
  366.                 try:
  367.                     self.copy_all()
  368.                 except EnvironmentError, e:
  369.                     if e.errno in (errno.ENOENT, errno.EIO, errno.EFAULT,
  370.                                    errno.ENOTDIR, errno.EROFS):
  371.                         if e.filename is None:
  372.                             error_template = 'cd_hd_fault'
  373.                         elif e.filename.startswith(self.target):
  374.                             error_template = 'hd_fault'
  375.                         else:
  376.                             error_template = 'cd_fault'
  377.                         error_template = ('ubiquity/install/copying_error/%s' %
  378.                                           error_template)
  379.                         self.db.subst(error_template, 'ERROR', str(e))
  380.                         self.db.input('critical', error_template)
  381.                         self.db.go()
  382.                         # Exit code 3 signals to the frontend that we have
  383.                         # handled this error.
  384.                         sys.exit(3)
  385.                     elif e.errno == errno.ENOSPC:
  386.                         error_template = 'ubiquity/install/copying_error/no_space'
  387.                         self.db.subst(error_template, 'ERROR', str(e))
  388.                         self.db.input('critical', error_template)
  389.                         self.db.go()
  390.                         sys.exit(3)
  391.                     else:
  392.                         raise
  393.  
  394.             self.db.progress('SET', count)
  395.             self.db.progress('REGION', count, count+1)
  396.             count += 1
  397.             self.db.progress('INFO', 'ubiquity/install/network')
  398.             self.configure_network()
  399.  
  400.             self.db.progress('SET', count)
  401.             self.db.progress('REGION', count, count+1)
  402.             count += 1
  403.             self.db.progress('INFO', 'ubiquity/install/apt')
  404.             self.configure_apt()
  405.  
  406.             self.configure_plugins(count)
  407.             count += len(self.plugins)
  408.  
  409.             self.db.progress('SET', count)
  410.             self.db.progress('REGION', count, count+1)
  411.             count += 1
  412.             self.db.progress('INFO', 'ubiquity/install/user')
  413.             self.configure_user()
  414.  
  415.             self.db.progress('SET', count)
  416.             self.db.progress('REGION', count, count+1)
  417.             count += 1
  418.             self.run_target_config_hooks()
  419.  
  420.             self.db.progress('SET', count)
  421.             self.db.progress('REGION', count, count+5)
  422.             count += 5
  423.             # Ignore failures from language pack installation.
  424.             try:
  425.                 self.install_language_packs()
  426.             except InstallStepError:
  427.                 pass
  428.             except IOError:
  429.                 pass
  430.             except SystemError:
  431.                 pass
  432.  
  433.             self.db.progress('SET', count)
  434.             self.db.progress('REGION', count, count+1)
  435.             count += 1
  436.             self.db.progress('INFO', 'ubiquity/install/migrationassistant')
  437.             self.configure_ma()
  438.  
  439.             self.db.progress('SET', count)
  440.             self.db.progress('REGION', count, count+1)
  441.             count += 1
  442.             self.remove_unusable_kernels()
  443.  
  444.             self.db.progress('SET', count)
  445.             self.db.progress('REGION', count, count+4)
  446.             count += 4
  447.             self.db.progress('INFO', 'ubiquity/install/hardware')
  448.             self.configure_hardware()
  449.  
  450.             # Tell apt-install to install packages directly from now on.
  451.             apt_install_direct = open('/var/lib/ubiquity/apt-install-direct',
  452.                                       'w')
  453.             apt_install_direct.close()
  454.  
  455.             self.db.progress('SET', count)
  456.             self.db.progress('REGION', count, count+1)
  457.             count += 1
  458.             self.db.progress('INFO', 'ubiquity/install/bootloader')
  459.             self.configure_bootloader()
  460.  
  461.             self.db.progress('SET', count)
  462.             self.db.progress('REGION', count, count+1)
  463.             count += 1
  464.             self.db.progress('INFO', 'ubiquity/install/installing')
  465.             self.install_extras()
  466.  
  467.             self.db.progress('SET', count)
  468.             self.db.progress('REGION', count, count+4)
  469.             count += 4
  470.             self.db.progress('INFO', 'ubiquity/install/removing')
  471.             self.remove_extras()
  472.  
  473.             self.remove_broken_cdrom()
  474.  
  475.             self.copy_dcd()
  476.             self.db.progress('SET', count)
  477.             self.db.progress('INFO', 'ubiquity/install/log_files')
  478.             self.copy_logs()
  479.  
  480.             self.db.progress('SET', end)
  481.         finally:
  482.             self.cleanup()
  483.             try:
  484.                 self.db.progress('STOP')
  485.             except (KeyboardInterrupt, SystemExit):
  486.                 raise
  487.             except:
  488.                 pass
  489.  
  490.  
  491.     def copy_file(self, sourcepath, targetpath, md5_check):
  492.         sourcefh = None
  493.         targetfh = None
  494.         try:
  495.             while 1:
  496.                 sourcefh = open(sourcepath, 'rb')
  497.                 targetfh = open(targetpath, 'wb')
  498.                 if md5_check:
  499.                     sourcehash = md5()
  500.                 while 1:
  501.                     buf = sourcefh.read(16 * 1024)
  502.                     if not buf:
  503.                         break
  504.                     targetfh.write(buf)
  505.                     if md5_check:
  506.                         sourcehash.update(buf)
  507.  
  508.                 if not md5_check:
  509.                     break
  510.                 targetfh.close()
  511.                 targetfh = open(targetpath, 'rb')
  512.                 if md5_check:
  513.                     targethash = md5()
  514.                 while 1:
  515.                     buf = targetfh.read(16 * 1024)
  516.                     if not buf:
  517.                         break
  518.                     targethash.update(buf)
  519.                 if targethash.digest() != sourcehash.digest():
  520.                     if targetfh:
  521.                         targetfh.close()
  522.                     if sourcefh:
  523.                         sourcefh.close()
  524.                     error_template = 'ubiquity/install/copying_error/md5'
  525.                     self.db.subst(error_template, 'FILE', targetpath)
  526.                     self.db.input('critical', error_template)
  527.                     self.db.go()
  528.                     response = self.db.get(error_template)
  529.                     if response == 'skip':
  530.                         break
  531.                     elif response == 'abort':
  532.                         syslog.syslog(syslog.LOG_ERR,
  533.                             'MD5 failure on %s' % targetpath)
  534.                         sys.exit(3)
  535.                     elif response == 'retry':
  536.                         pass
  537.                 else:
  538.                     break
  539.         finally:
  540.             if targetfh:
  541.                 targetfh.close()
  542.             if sourcefh:
  543.                 sourcefh.close()
  544.  
  545.     def find_cd_kernel(self):
  546.         """Find the boot kernel on the CD, if possible."""
  547.  
  548.         release_bits = os.uname()[2].split('-')
  549.         if len(release_bits) >= 3:
  550.             subarch = release_bits[2]
  551.         else:
  552.             subarch = None
  553.  
  554.         for prefix in ('vmlinux', 'vmlinuz'):
  555.             kernel = os.path.join(self.casper_path, prefix)
  556.             if os.path.exists(kernel):
  557.                 return kernel
  558.  
  559.             if subarch:
  560.                 kernel = os.path.join(self.casper_path, subarch, prefix)
  561.                 if os.path.exists(kernel):
  562.                     return kernel
  563.  
  564.                 kernel = os.path.join(self.casper_path,
  565.                                       '%s-%s' % (prefix, subarch))
  566.                 if os.path.exists(kernel):
  567.                     return kernel
  568.  
  569.         return None
  570.  
  571.  
  572.     def archdetect(self):
  573.         archdetect = subprocess.Popen(['archdetect'], stdout=subprocess.PIPE)
  574.         answer = archdetect.communicate()[0].strip()
  575.         try:
  576.             return answer.split('/', 1)
  577.         except ValueError:
  578.             return answer, ''
  579.  
  580.  
  581.     def generate_blacklist(self):
  582.         manifest_desktop = os.path.join(self.casper_path,
  583.                                         'filesystem.manifest-desktop')
  584.         manifest = os.path.join(self.casper_path, 'filesystem.manifest')
  585.         if (os.path.exists(manifest_desktop) and
  586.             os.path.exists(manifest)):
  587.             desktop_packages = set()
  588.             manifest_file = open(manifest_desktop)
  589.             for line in manifest_file:
  590.                 if line.strip() != '' and not line.startswith('#'):
  591.                     desktop_packages.add(line.split()[0])
  592.             manifest_file.close()
  593.             live_packages = set()
  594.             manifest_file = open(manifest)
  595.             for line in manifest_file:
  596.                 if line.strip() != '' and not line.startswith('#'):
  597.                     live_packages.add(line.split()[0])
  598.             manifest_file.close()
  599.             difference = live_packages - desktop_packages
  600.         else:
  601.             difference = set()
  602.  
  603.         cache = Cache()
  604.  
  605.         use_restricted = True
  606.         try:
  607.             if self.db.get('apt-setup/restricted') == 'false':
  608.                 use_restricted = False
  609.         except debconf.DebconfError:
  610.             pass
  611.         if not use_restricted:
  612.             for pkg in cache.keys():
  613.                 if (cache[pkg].isInstalled and
  614.                     cache[pkg].section.startswith('restricted/')):
  615.                     difference.add(pkg)
  616.  
  617.         # Keep packages we explicitly installed.
  618.         keep = self.query_recorded_installed()
  619.         arch, subarch = self.archdetect()
  620.  
  621.         # Less than ideal.  Since we cannot know which bootloader we'll need
  622.         # at file copy time, we should figure out why grub still fails when
  623.         # apt-install-direct is present during configure_bootloader (code
  624.         # removed).
  625.         if arch in ('amd64', 'i386', 'lpia'):
  626.             keep.add('grub')
  627.             keep.add('grub-pc')
  628.         elif (arch == 'armel' and
  629.               subarch in ('dove', 'imx51', 'iop32x', 'ixp4xx', 'orion5x')):
  630.             keep.add('flash-kernel')
  631.         elif arch == 'powerpc' and subarch != 'ps3':
  632.             keep.add('yaboot')
  633.             keep.add('hfsutils')
  634.  
  635.         #Even adding ubiquity as a depends to oem-config-{gtk,kde}
  636.         #doesn't appear to force ubiquity and libdebian-installer4
  637.         #to copy all of their files, so this does the trick.
  638.         try:
  639.             if self.db.get('oem-config/enable') == 'true':
  640.                 keep.add('ubiquity')
  641.         except (debconf.DebconfError, IOError):
  642.             pass
  643.  
  644.         difference -= self.expand_dependencies_simple(cache, keep, difference)
  645.  
  646.         # Consider only packages that don't have a prerm, and which can
  647.         # therefore have their files removed without any preliminary work.
  648.         difference = set(filter(
  649.             lambda x: not os.path.exists('/var/lib/dpkg/info/%s.prerm' % x),
  650.             difference))
  651.  
  652.         confirmed_remove = set()
  653.         for pkg in sorted(difference):
  654.             if pkg in confirmed_remove:
  655.                 continue
  656.             would_remove = self.get_remove_list(cache, [pkg], recursive=True)
  657.             if would_remove <= difference:
  658.                 confirmed_remove |= would_remove
  659.                 # Leave these marked for removal in the apt cache to speed
  660.                 # up further calculations.
  661.             else:
  662.                 for removedpkg in would_remove:
  663.                     cachedpkg = self.get_cache_pkg(cache, removedpkg)
  664.                     cachedpkg.markKeep()
  665.         difference = confirmed_remove
  666.  
  667.         if len(difference) == 0:
  668.             del cache
  669.             self.blacklist = {}
  670.             return
  671.  
  672.         cmd = ['dpkg', '-L']
  673.         cmd.extend(difference)
  674.         subp = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
  675.         res = subp.communicate()[0].splitlines()
  676.         u = {}
  677.         for x in res:
  678.             u[x] = 1
  679.         self.blacklist = u
  680.  
  681.     def copy_all(self):
  682.         """Core copy process. This is the most important step of this
  683.         stage. It clones live filesystem into a local partition in the
  684.         selected hard disk."""
  685.  
  686.         files = []
  687.         total_size = 0
  688.  
  689.         self.db.progress('START', 0, 100, 'ubiquity/install/title')
  690.         self.db.progress('INFO', 'ubiquity/install/scanning')
  691.  
  692.         # Obviously doing os.walk() twice is inefficient, but I'd rather not
  693.         # suck the list into ubiquity's memory, and I'm guessing that the
  694.         # kernel's dentry cache will avoid most of the slowness anyway.
  695.         walklen = 0
  696.         for entry in os.walk(self.source):
  697.             walklen += 1
  698.         walkpos = 0
  699.         walkprogress = 0
  700.  
  701.         for dirpath, dirnames, filenames in os.walk(self.source):
  702.             walkpos += 1
  703.             if int(float(walkpos) / walklen * 10) != walkprogress:
  704.                 walkprogress = int(float(walkpos) / walklen * 10)
  705.                 self.db.progress('SET', walkprogress)
  706.  
  707.             sourcepath = dirpath[len(self.source) + 1:]
  708.  
  709.             for name in dirnames + filenames:
  710.                 relpath = os.path.join(sourcepath, name)
  711.                 fqpath = os.path.join(dirpath, name)
  712.                 # /etc/fstab was legitimately created by partman, and
  713.                 # shouldn't be copied again.
  714.                 if relpath != "etc/fstab":
  715.                     total_size += os.lstat(fqpath).st_size
  716.                     files.append(relpath)
  717.  
  718.         self.db.progress('SET', 10)
  719.         self.db.progress('INFO', 'ubiquity/install/copying')
  720.  
  721.         # Progress bar handling:
  722.         # We sample progress every half-second (assuming time.time() gives
  723.         # us sufficiently good granularity) and use the average of progress
  724.         # over the last minute or so to decide how much time remains. We
  725.         # don't bother displaying any progress for the first ten seconds in
  726.         # order to allow things to settle down, and we only update the "time
  727.         # remaining" indicator at most every two seconds after that.
  728.  
  729.         copy_progress = 0
  730.         copied_size, counter = 0, 0
  731.         directory_times = []
  732.         time_start = time.time()
  733.         times = [(time_start, copied_size)]
  734.         long_enough = False
  735.         time_last_update = time_start
  736.         if self.db.get('ubiquity/install/md5_check') == 'false':
  737.             md5_check = False
  738.         else:
  739.             md5_check = True
  740.         
  741.         old_umask = os.umask(0)
  742.         for path in files:
  743.             sourcepath = os.path.join(self.source, path)
  744.             targetpath = os.path.join(self.target, path)
  745.             st = os.lstat(sourcepath)
  746.             mode = stat.S_IMODE(st.st_mode)
  747.             if stat.S_ISLNK(st.st_mode):
  748.                 if os.path.lexists(targetpath):
  749.                     os.unlink(targetpath)
  750.                 linkto = os.readlink(sourcepath)
  751.                 os.symlink(linkto, targetpath)
  752.             elif stat.S_ISDIR(st.st_mode):
  753.                 if not os.path.isdir(targetpath):
  754.                     os.mkdir(targetpath, mode)
  755.             elif stat.S_ISCHR(st.st_mode):
  756.                 os.mknod(targetpath, stat.S_IFCHR | mode, st.st_rdev)
  757.             elif stat.S_ISBLK(st.st_mode):
  758.                 os.mknod(targetpath, stat.S_IFBLK | mode, st.st_rdev)
  759.             elif stat.S_ISFIFO(st.st_mode):
  760.                 os.mknod(targetpath, stat.S_IFIFO | mode)
  761.             elif stat.S_ISSOCK(st.st_mode):
  762.                 os.mknod(targetpath, stat.S_IFSOCK | mode)
  763.             elif stat.S_ISREG(st.st_mode):
  764.                 if '/%s' % path in self.blacklist:
  765.                     syslog.syslog('Not copying %s' % path)
  766.                     continue
  767.                 if os.path.exists(targetpath):
  768.                     os.unlink(targetpath)
  769.                 self.copy_file(sourcepath, targetpath, md5_check)
  770.  
  771.             copied_size += st.st_size
  772.             os.lchown(targetpath, st.st_uid, st.st_gid)
  773.             if not stat.S_ISLNK(st.st_mode):
  774.                 os.chmod(targetpath, mode)
  775.             if stat.S_ISDIR(st.st_mode):
  776.                 directory_times.append((targetpath, st.st_atime, st.st_mtime))
  777.             # os.utime() sets timestamp of target, not link
  778.             elif not stat.S_ISLNK(st.st_mode):
  779.                 os.utime(targetpath, (st.st_atime, st.st_mtime))
  780.  
  781.             if int((copied_size * 90) / total_size) != copy_progress:
  782.                 copy_progress = int((copied_size * 90) / total_size)
  783.                 self.db.progress('SET', 10 + copy_progress)
  784.  
  785.             time_now = time.time()
  786.             if (time_now - times[-1][0]) >= 0.5:
  787.                 times.append((time_now, copied_size))
  788.                 if not long_enough and time_now - times[0][0] >= 10:
  789.                     long_enough = True
  790.                 if long_enough and time_now - time_last_update >= 2:
  791.                     time_last_update = time_now
  792.                     while (time_now - times[0][0] > 60 and
  793.                            time_now - times[1][0] >= 60):
  794.                         times.pop(0)
  795.                     speed = ((times[-1][1] - times[0][1]) /
  796.                              (times[-1][0] - times[0][0]))
  797.                     if speed != 0:
  798.                         time_remaining = int((total_size - copied_size) / speed)
  799.                         if time_remaining < 60:
  800.                             self.db.progress(
  801.                                 'INFO', 'ubiquity/install/copying_minute')
  802.  
  803.         # Apply timestamps to all directories now that the items within them
  804.         # have been copied.
  805.         for dirtime in directory_times:
  806.             (directory, atime, mtime) = dirtime
  807.             try:
  808.                 os.utime(directory, (atime, mtime))
  809.             except OSError:
  810.                 # I have no idea why I've been getting lots of bug reports
  811.                 # about this failing, but I really don't care. Ignore it.
  812.                 pass
  813.  
  814.         # Try some possible locations for the kernel we used to boot. This
  815.         # lets us save a couple of megabytes of CD space.
  816.         bootdir = os.path.join(self.target, 'boot')
  817.         kernel = self.find_cd_kernel()
  818.         if kernel:
  819.             prefix = os.path.basename(kernel).split('-', 1)[0]
  820.             release = os.uname()[2]
  821.             target_kernel = os.path.join(bootdir, '%s-%s' % (prefix, release))
  822.             if os.path.exists(target_kernel):
  823.                 os.unlink(target_kernel)
  824.             self.copy_file(kernel, target_kernel, md5_check)
  825.             os.lchown(target_kernel, 0, 0)
  826.             os.chmod(target_kernel, 0644)
  827.             st = os.lstat(kernel)
  828.             os.utime(target_kernel, (st.st_atime, st.st_mtime))
  829.  
  830.         os.umask(old_umask)
  831.  
  832.         self.db.progress('SET', 100)
  833.         self.db.progress('STOP')
  834.  
  835.  
  836.     def copy_dcd(self):
  837.         """Copy the Distribution Channel Descriptor (DCD) file into the
  838.         installed system."""
  839.  
  840.         dcd = '/cdrom/.disk/ubuntu_dist_channel'
  841.         if os.path.exists(dcd):
  842.             shutil.copy(dcd,
  843.                 os.path.join(self.target, 'var/lib/ubuntu_dist_channel'))
  844.  
  845.     def copy_logs(self):
  846.         """copy log files into installed system."""
  847.  
  848.         if 'UBIQUITY_OEM_USER_CONFIG' in os.environ:
  849.             return
  850.  
  851.         target_dir = os.path.join(self.target, 'var/log/installer')
  852.         if not os.path.exists(target_dir):
  853.             os.makedirs(target_dir)
  854.  
  855.         for log_file in ('/var/log/syslog', '/var/log/partman',
  856.                          '/var/log/installer/version', '/var/log/casper.log',
  857.                          '/var/log/installer/debug'):
  858.             target_log_file = os.path.join(target_dir,
  859.                                            os.path.basename(log_file))
  860.             if os.path.isfile(log_file):
  861.                 if not misc.execute('cp', '-a', log_file, target_log_file):
  862.                     syslog.syslog(syslog.LOG_ERR,
  863.                                   'Failed to copy installation log file')
  864.                 os.chmod(target_log_file, stat.S_IRUSR | stat.S_IWUSR)
  865.         media_info = '/cdrom/.disk/info'
  866.         if os.path.isfile(media_info):
  867.             try:
  868.                 shutil.copy(media_info,
  869.                     os.path.join(self.target, 'var/log/installer/media-info'))
  870.             except (IOError, OSError):
  871.                 pass
  872.                 
  873.         try:
  874.             status = open(os.path.join(self.target, 'var/lib/dpkg/status'))
  875.             status_gz = gzip.open(os.path.join(target_dir,
  876.                                                'initial-status.gz'), 'w')
  877.             while True:
  878.                 data = status.read(65536)
  879.                 if not data:
  880.                     break
  881.                 status_gz.write(data)
  882.             status_gz.close()
  883.             status.close()
  884.         except IOError:
  885.             pass
  886.         try:
  887.             if self.db.get('oem-config/enable') == 'true':
  888.                 oem_id = self.db.get('oem-config/id')
  889.                 oem_id_file = open(
  890.                     os.path.join(self.target, 'var/log/installer/oem-id'), 'w')
  891.                 print >>oem_id_file, oem_id
  892.                 oem_id_file.close()
  893.         except (debconf.DebconfError, IOError):
  894.             pass
  895.  
  896.  
  897.     def mount_one_image(self, fsfile, mountpoint=None):
  898.         if os.path.splitext(fsfile)[1] == '.cloop':
  899.             blockdev_prefix = 'cloop'
  900.         elif os.path.splitext(fsfile)[1] == '.squashfs':
  901.             blockdev_prefix = 'loop'
  902.  
  903.         if blockdev_prefix == '':
  904.             raise InstallStepError("No source device found for %s" % fsfile)
  905.  
  906.         dev = ''
  907.         sysloops = filter(lambda x: x.startswith(blockdev_prefix),
  908.                           os.listdir('/sys/block'))
  909.         sysloops.sort()
  910.         for sysloop in sysloops:
  911.             try:
  912.                 sysloopf = open(os.path.join('/sys/block', sysloop, 'size'))
  913.                 sysloopsize = sysloopf.readline().strip()
  914.                 sysloopf.close()
  915.                 if sysloopsize == '0':
  916.                     devnull = open('/dev/null')
  917.                     if osextras.find_on_path('udevadm'):
  918.                         udevinfo_cmd = ['udevadm', 'info']
  919.                     else:
  920.                         udevinfo_cmd = ['udevinfo']
  921.                     udevinfo_cmd.extend(
  922.                         ['-q', 'name', '-p', os.path.join('/block', sysloop)])
  923.                     udevinfo = subprocess.Popen(
  924.                         udevinfo_cmd, stdout=subprocess.PIPE, stderr=devnull)
  925.                     devbase = udevinfo.communicate()[0]
  926.                     devnull.close()
  927.                     if udevinfo.returncode != 0:
  928.                         devbase = sysloop
  929.                     dev = '/dev/%s' % devbase
  930.                     break
  931.             except:
  932.                 continue
  933.  
  934.         if dev == '':
  935.             raise InstallStepError("No loop device available for %s" % fsfile)
  936.  
  937.         misc.execute('losetup', dev, fsfile)
  938.         if mountpoint is None:
  939.             mountpoint = '/var/lib/ubiquity/%s' % sysloop
  940.         if not os.path.isdir(mountpoint):
  941.             os.mkdir(mountpoint)
  942.         if not misc.execute('mount', dev, mountpoint):
  943.             misc.execute('losetup', '-d', dev)
  944.             misc.execute('mount', '-o', 'loop', fsfile, mountpoint)
  945.             dev = 'unused'
  946.  
  947.         return (dev, mountpoint)
  948.  
  949.     def mount_source(self):
  950.         """mounting loop system from cloop or squashfs system."""
  951.  
  952.         self.devs = []
  953.         self.mountpoints = []
  954.  
  955.         if not os.path.isdir(self.source):
  956.             syslog.syslog('mkdir %s' % self.source)
  957.             os.mkdir(self.source)
  958.  
  959.         fs_preseed = self.db.get('ubiquity/install/filesystem-images')
  960.  
  961.         if fs_preseed == '':
  962.             # Simple autodetection on unionfs systems
  963.             mounts = open('/proc/mounts')
  964.             for line in mounts:
  965.                 (device, fstype) = line.split()[1:3]
  966.                 if fstype == 'squashfs' and os.path.exists(device):
  967.                     misc.execute('mount', '--bind', device, self.source)
  968.                     self.mountpoints.append(self.source)
  969.                     mounts.close()
  970.                     return
  971.             mounts.close()
  972.  
  973.             # Manual detection on non-unionfs systems
  974.             fsfiles = [os.path.join(self.casper_path, 'filesystem.cloop'),
  975.                        os.path.join(self.casper_path, 'filesystem.squashfs'),
  976.                        '/cdrom/META/META.squashfs',
  977.                        '/live/image/live/filesystem.squashfs']
  978.  
  979.             for fsfile in fsfiles:
  980.                 if fsfile != '' and os.path.isfile(fsfile):
  981.                     dev, mountpoint = self.mount_one_image(fsfile, self.source)
  982.                     self.devs.append(dev)
  983.                     self.mountpoints.append(mountpoint)
  984.  
  985.         elif len(fs_preseed.split()) == 1:
  986.             # Just one preseeded image.
  987.             if not os.path.isfile(fs_preseed):
  988.                 raise InstallStepError(
  989.                     "Preseeded filesystem image %s not found" % fs_preseed)
  990.  
  991.                 dev, mountpoint = self.mount_one_image(fsfile, self.source)
  992.                 self.devs.append(dev)
  993.                 self.mountpoints.append(mountpoint)
  994.         else:
  995.             # OK, so we need to mount multiple images and unionfs them
  996.             # together.
  997.             for fsfile in fs_preseed.split():
  998.                 if not os.path.isfile(fsfile):
  999.                     raise InstallStepError(
  1000.                         "Preseeded filesystem image %s not found" % fsfile)
  1001.  
  1002.                 dev, mountpoint = self.mount_one_image(fsfile)
  1003.                 self.devs.append(dev)
  1004.                 self.mountpoints.append(mountpoint)
  1005.  
  1006.             assert self.devs
  1007.             assert self.mountpoints
  1008.  
  1009.             misc.execute('mount', '-t', 'unionfs', '-o',
  1010.                          'dirs=' + ':'.join(map(lambda x: '%s=ro' % x,
  1011.                                                 self.mountpoints)),
  1012.                          'unionfs', self.source)
  1013.             self.mountpoints.append(self.source)
  1014.  
  1015.     def umount_source(self):
  1016.         """umounting loop system from cloop or squashfs system."""
  1017.  
  1018.         devs = self.devs
  1019.         devs.reverse()
  1020.         mountpoints = self.mountpoints
  1021.         mountpoints.reverse()
  1022.  
  1023.         for mountpoint in mountpoints:
  1024.             if not misc.execute('umount', mountpoint):
  1025.                 raise InstallStepError("Failed to unmount %s" % mountpoint)
  1026.         for dev in devs:
  1027.             if (dev != '' and dev != 'unused' and
  1028.                 not misc.execute('losetup', '-d', dev)):
  1029.                 raise InstallStepError(
  1030.                     "Failed to detach loopback device %s" % dev)
  1031.  
  1032.  
  1033.     def chroot_setup(self, x11=False):
  1034.         """Set up /target for safe package management operations."""
  1035.         if self.target == '/':
  1036.             return
  1037.  
  1038.         policy_rc_d = os.path.join(self.target, 'usr/sbin/policy-rc.d')
  1039.         f = open(policy_rc_d, 'w')
  1040.         print >>f, """\
  1041. #!/bin/sh
  1042. exit 101"""
  1043.         f.close()
  1044.         os.chmod(policy_rc_d, 0755)
  1045.  
  1046.         start_stop_daemon = os.path.join(self.target, 'sbin/start-stop-daemon')
  1047.         if os.path.exists(start_stop_daemon):
  1048.             os.rename(start_stop_daemon, '%s.REAL' % start_stop_daemon)
  1049.         f = open(start_stop_daemon, 'w')
  1050.         print >>f, """\
  1051. #!/bin/sh
  1052. echo 1>&2
  1053. echo 'Warning: Fake start-stop-daemon called, doing nothing.' 1>&2
  1054. exit 0"""
  1055.         f.close()
  1056.         os.chmod(start_stop_daemon, 0755)
  1057.  
  1058.         initctl = os.path.join(self.target, 'sbin/initctl')
  1059.         if os.path.exists(initctl):
  1060.             os.rename(initctl, '%s.REAL' % initctl)
  1061.             f = open(initctl, 'w')
  1062.             print >>f, """\
  1063. #!/bin/sh
  1064. echo 1>&2
  1065. echo 'Warning: Fake initctl called, doing nothing.' 1>&2
  1066. exit 0"""
  1067.             f.close()
  1068.             os.chmod(initctl, 0755)
  1069.  
  1070.         if not os.path.exists(os.path.join(self.target, 'proc/cmdline')):
  1071.             self.chrex('mount', '-t', 'proc', 'proc', '/proc')
  1072.         if not os.path.exists(os.path.join(self.target, 'sys/devices')):
  1073.             self.chrex('mount', '-t', 'sysfs', 'sysfs', '/sys')
  1074.         misc.execute('mount', '--bind', '/dev', os.path.join(self.target, 'dev'))
  1075.  
  1076.         if x11 and 'DISPLAY' in os.environ:
  1077.             if 'SUDO_USER' in os.environ:
  1078.                 xauthority = os.path.expanduser('~%s/.Xauthority' %
  1079.                                                 os.environ['SUDO_USER'])
  1080.             else:
  1081.                 xauthority = os.path.expanduser('~/.Xauthority')
  1082.             if os.path.exists(xauthority):
  1083.                 shutil.copy(xauthority,
  1084.                             os.path.join(self.target, 'root/.Xauthority'))
  1085.  
  1086.             if not os.path.isdir(os.path.join(self.target, 'tmp/.X11-unix')):
  1087.                 os.mkdir(os.path.join(self.target, 'tmp/.X11-unix'))
  1088.             misc.execute('mount', '--bind', '/tmp/.X11-unix',
  1089.                          os.path.join(self.target, 'tmp/.X11-unix'))
  1090.  
  1091.     def chroot_cleanup(self, x11=False):
  1092.         """Undo the work done by chroot_setup."""
  1093.         if self.target == '/':
  1094.             return
  1095.  
  1096.         if x11 and 'DISPLAY' in os.environ:
  1097.             misc.execute('umount', os.path.join(self.target, 'tmp/.X11-unix'))
  1098.             try:
  1099.                 os.rmdir(os.path.join(self.target, 'tmp/.X11-unix'))
  1100.             except OSError:
  1101.                 pass
  1102.             try:
  1103.                 os.unlink(os.path.join(self.target, 'root/.Xauthority'))
  1104.             except OSError:
  1105.                 pass
  1106.  
  1107.         self.chrex('umount', '/dev')
  1108.         self.chrex('umount', '/sys')
  1109.         self.chrex('umount', '/proc')
  1110.  
  1111.         initctl = os.path.join(self.target, 'sbin/initctl')
  1112.         if os.path.exists('%s.REAL' % initctl):
  1113.             os.rename('%s.REAL' % initctl, initctl)
  1114.  
  1115.         start_stop_daemon = os.path.join(self.target, 'sbin/start-stop-daemon')
  1116.         if os.path.exists('%s.REAL' % start_stop_daemon):
  1117.             os.rename('%s.REAL' % start_stop_daemon, start_stop_daemon)
  1118.         else:
  1119.             os.unlink(start_stop_daemon)
  1120.  
  1121.         policy_rc_d = os.path.join(self.target, 'usr/sbin/policy-rc.d')
  1122.         os.unlink(policy_rc_d)
  1123.  
  1124.  
  1125.     def run_target_config_hooks(self):
  1126.         """Run hook scripts from /usr/lib/ubiquity/target-config. This allows
  1127.         casper to hook into us and repeat bits of its configuration in the
  1128.         target system."""
  1129.  
  1130.         hookdir = '/usr/lib/ubiquity/target-config'
  1131.  
  1132.         if os.path.isdir(hookdir):
  1133.             # Exclude hooks containing '.', so that *.dpkg-* et al are avoided.
  1134.             hooks = filter(lambda entry: '.' not in entry, os.listdir(hookdir))
  1135.             self.db.progress('START', 0, len(hooks), 'ubiquity/install/title')
  1136.             self.db.progress('INFO', 'ubiquity/install/target_hooks')
  1137.             for hookentry in hooks:
  1138.                 hook = os.path.join(hookdir, hookentry)
  1139.                 if not os.access(hook, os.X_OK):
  1140.                     self.db.progress('STEP', 1)
  1141.                     continue
  1142.                 # Errors are ignored at present, although this may change.
  1143.                 subprocess.call(['log-output', '-t', 'ubiquity',
  1144.                                  '--pass-stdout', hook])
  1145.                 self.db.progress('STEP', 1)
  1146.             self.db.progress('STOP')
  1147.  
  1148.  
  1149.     def configure_plugins(self, count):
  1150.         """Apply plugin settings to installed system."""
  1151.         class Progress:
  1152.             def __init__(self, db):
  1153.                 self._db = db
  1154.             def info(self, title):
  1155.                 self._db.progress('INFO', title)
  1156.  
  1157.         for plugin in self.plugins:
  1158.             self.db.progress('SET', count)
  1159.             self.db.progress('REGION', count, count+1)
  1160.             count += 1
  1161.             self.db.progress('INFO', ' ') # clear info in case plugin doesn't provide one
  1162.             inst = plugin.Install(None, db=self.db)
  1163.             ret = inst.install(self.target, Progress(self.db))
  1164.             if ret:
  1165.                 raise InstallStepError("Plugin %s failed with code %s" % (plugin.NAME, ret))
  1166.  
  1167.  
  1168.     def configure_apt(self):
  1169.         """Configure /etc/apt/sources.list."""
  1170.  
  1171.         if 'UBIQUITY_OEM_USER_CONFIG' in os.environ:
  1172.             return # apt will already be setup as the OEM wants
  1173.  
  1174.         # TODO cjwatson 2007-07-06: Much of the following is
  1175.         # cloned-and-hacked from base-installer/debian/postinst. Perhaps we
  1176.         # should come up with a way to avoid this.
  1177.  
  1178.         # Make apt trust CDs. This is not on by default (we think).
  1179.         # This will be left in place on the installed system.
  1180.         apt_conf_tc = open(os.path.join(
  1181.             self.target, 'etc/apt/apt.conf.d/00trustcdrom'), 'w')
  1182.         print >>apt_conf_tc, 'APT::Authentication::TrustCDROM "true";'
  1183.         apt_conf_tc.close()
  1184.  
  1185.         # Avoid clock skew causing gpg verification issues.
  1186.         # This file will be left in place until the end of the install.
  1187.         apt_conf_itc = open(os.path.join(
  1188.             self.target, 'etc/apt/apt.conf.d/00IgnoreTimeConflict'), 'w')
  1189.         print >>apt_conf_itc, \
  1190.             'Acquire::gpgv::Options { "--ignore-time-conflict"; };'
  1191.         apt_conf_itc.close()
  1192.  
  1193.         try:
  1194.             if self.db.get('debian-installer/allow_unauthenticated') == 'true':
  1195.                 apt_conf_au = open(
  1196.                     os.path.join(self.target,
  1197.                                  'etc/apt/apt.conf.d/00AllowUnauthenticated'),
  1198.                     'w')
  1199.                 print >>apt_conf_au, 'APT::Get::AllowUnauthenticated "true";'
  1200.                 print >>apt_conf_au, \
  1201.                     'Aptitude::CmdLine::Ignore-Trust-Violations "true";'
  1202.                 apt_conf_au.close()
  1203.         except debconf.DebconfError:
  1204.             pass
  1205.  
  1206.         # let apt inside the chroot see the cdrom
  1207.         if self.target != "/":
  1208.             target_cdrom = os.path.join(self.target, 'cdrom')
  1209.             misc.execute('umount', target_cdrom)
  1210.             if not os.path.exists(target_cdrom):
  1211.                 os.mkdir(target_cdrom)
  1212.             misc.execute('mount', '--bind', '/cdrom', target_cdrom)
  1213.  
  1214.         # Make apt-cdrom and apt not unmount/mount CD-ROMs.
  1215.         # This file will be left in place until the end of the install.
  1216.         apt_conf_nmc = open(os.path.join(
  1217.             self.target, 'etc/apt/apt.conf.d/00NoMountCDROM'), 'w')
  1218.         print >>apt_conf_nmc, textwrap.dedent("""\
  1219.             APT::CDROM::NoMount "true";
  1220.             Acquire::cdrom {
  1221.               mount "/cdrom";
  1222.               "/cdrom/" {
  1223.                 Mount  "true";
  1224.                 UMount "true";
  1225.               };
  1226.             }""")
  1227.         apt_conf_nmc.close()
  1228.  
  1229.         # This will be reindexed after installation based on the full
  1230.         # installed sources.list.
  1231.         try:
  1232.             shutil.rmtree(
  1233.                 os.path.join(self.target, 'var/lib/apt-xapian-index'),
  1234.                 ignore_errors=True)
  1235.         except OSError:
  1236.             pass
  1237.  
  1238.         dbfilter = apt_setup.AptSetup(None, self.db)
  1239.         ret = dbfilter.run_command(auto_process=True)
  1240.         if ret != 0:
  1241.             raise InstallStepError("AptSetup failed with code %d" % ret)
  1242.  
  1243.  
  1244.     def get_cache_pkg(self, cache, pkg):
  1245.         # work around broken has_key in python-apt 0.6.16
  1246.         try:
  1247.             return cache[pkg]
  1248.         except KeyError:
  1249.             return None
  1250.  
  1251.  
  1252.     def record_installed(self, pkgs):
  1253.         """Record which packages we've explicitly installed so that we don't
  1254.         try to remove them later."""
  1255.  
  1256.         record_file = "/var/lib/ubiquity/apt-installed"
  1257.         if not os.path.exists(os.path.dirname(record_file)):
  1258.             os.makedirs(os.path.dirname(record_file))
  1259.         record = open(record_file, "a")
  1260.  
  1261.         for pkg in pkgs:
  1262.             print >>record, pkg
  1263.  
  1264.         record.close()
  1265.  
  1266.  
  1267.     def query_recorded_installed(self):
  1268.         apt_installed = set()
  1269.         if os.path.exists("/var/lib/ubiquity/apt-installed"):
  1270.             record_file = open("/var/lib/ubiquity/apt-installed")
  1271.             for line in record_file:
  1272.                 apt_installed.add(line.strip())
  1273.             record_file.close()
  1274.         return apt_installed
  1275.  
  1276.  
  1277.     def mark_install(self, cache, pkg):
  1278.         cachedpkg = self.get_cache_pkg(cache, pkg)
  1279.         if cachedpkg is not None and not cachedpkg.isInstalled:
  1280.             apt_error = False
  1281.             try:
  1282.                 cachedpkg.markInstall()
  1283.             except SystemError:
  1284.                 apt_error = True
  1285.             if cache._depcache.BrokenCount > 0 or apt_error:
  1286.                 brokenpkgs = self.broken_packages(cache)
  1287.                 while brokenpkgs:
  1288.                     for brokenpkg in brokenpkgs:
  1289.                         self.get_cache_pkg(cache, brokenpkg).markKeep()
  1290.                     new_brokenpkgs = self.broken_packages(cache)
  1291.                     if brokenpkgs == new_brokenpkgs:
  1292.                         break # we can do nothing more
  1293.                     brokenpkgs = new_brokenpkgs
  1294.                 assert cache._depcache.BrokenCount == 0
  1295.  
  1296.  
  1297.     def select_language_packs(self):
  1298.         try:
  1299.             keep_packages = self.db.get('ubiquity/keep-installed')
  1300.             keep_packages = keep_packages.replace(',', '').split()
  1301.             syslog.syslog('keeping packages due to preseeding: %s' %
  1302.                           ' '.join(keep_packages))
  1303.             self.record_installed(keep_packages)
  1304.         except debconf.DebconfError:
  1305.             pass
  1306.  
  1307.         langpacks = []
  1308.         all_langpacks = False
  1309.         try:
  1310.             langpack_db = self.db.get('pkgsel/language-packs')
  1311.             if langpack_db == 'ALL':
  1312.                 apt_out = subprocess.Popen(
  1313.                     ['apt-cache', '-n', 'search', '^language-pack-[^-][^-]*$'],
  1314.                     stdout=subprocess.PIPE).communicate()[0].rstrip().split('\n')
  1315.                 langpacks = map(lambda x: x.split('-')[2].strip(), apt_out)
  1316.                 all_langpacks = True
  1317.             else:
  1318.                 langpacks = langpack_db.replace(',', '').split()
  1319.         except debconf.DebconfError:
  1320.             pass
  1321.         if not langpacks:
  1322.             try:
  1323.                 langpack_db = self.db.get('localechooser/supported-locales')
  1324.                 langpack_set = set()
  1325.                 for locale in langpack_db.replace(',', '').split():
  1326.                     langpack_set.add(locale.split('_')[0])
  1327.                 langpacks = sorted(langpack_set)
  1328.             except debconf.DebconfError:
  1329.                 pass
  1330.         if not langpacks:
  1331.             langpack_db = self.db.get('debian-installer/locale')
  1332.             langpacks = [langpack_db.split('_')[0]]
  1333.         self.languages = langpacks
  1334.         syslog.syslog('keeping language packs for: %s' % ' '.join(langpacks))
  1335.  
  1336.         try:
  1337.             lppatterns = self.db.get('pkgsel/language-pack-patterns').split()
  1338.         except debconf.DebconfError:
  1339.             return
  1340.  
  1341.         cache = Cache()
  1342.  
  1343.         to_install = []
  1344.         checker = osextras.find_on_path('check-language-support')
  1345.         for lp in langpacks:
  1346.             # Basic language packs, required to get localisation working at
  1347.             # all. We install these almost unconditionally; if you want to
  1348.             # get rid of even these, you can preseed pkgsel/language-packs
  1349.             # to the empty string.
  1350.             to_install.append('language-pack-%s' % lp)
  1351.             # Other language packs, typically selected by preseeding.
  1352.             for pattern in lppatterns:
  1353.                 to_install.append(pattern.replace('$LL', lp))
  1354.             # More extensive language support packages.
  1355.             # If pkgsel/language-packs is ALL, then speed things up by
  1356.             # calling check-language-support just once.
  1357.             if not all_langpacks and checker:
  1358.                 check_lang = subprocess.Popen(
  1359.                     ['check-language-support', '-l', lp, '--show-installed'],
  1360.                     stdout=subprocess.PIPE)
  1361.                 to_install.extend(check_lang.communicate()[0].strip().split())
  1362.             else:
  1363.                 to_install.append('language-support-%s' % lp)
  1364.             if checker:
  1365.                 # Keep language-support-$LL installed if it happens to be in
  1366.                 # the live filesystem, since there's no point spending time
  1367.                 # removing it; but don't install it if it isn't in the live
  1368.                 # filesystem.
  1369.                 toplevel = 'language-support-%s' % lp
  1370.                 toplevel_pkg = self.get_cache_pkg(cache, toplevel)
  1371.                 if toplevel_pkg and toplevel_pkg.isInstalled:
  1372.                     to_install.append(toplevel)
  1373.         if all_langpacks and osextras.find_on_path('check-language-support'):
  1374.             check_lang = subprocess.Popen(
  1375.                 ['check-language-support', '-a', '--show-installed'],
  1376.                 stdout=subprocess.PIPE)
  1377.             to_install.extend(check_lang.communicate()[0].strip().split())
  1378.  
  1379.         # Filter the list of language packs to include only language packs
  1380.         # that exist in the live filesystem's apt cache, so that we can tell
  1381.         # the difference between "no such language pack" and "language pack
  1382.         # not retrievable given apt configuration in /target" later on.
  1383.         to_install = [lp for lp in to_install
  1384.                          if self.get_cache_pkg(cache, lp) is not None]
  1385.  
  1386.         del cache
  1387.  
  1388.         self.record_installed(to_install)
  1389.         self.langpacks = to_install
  1390.  
  1391.     def install_language_packs(self):
  1392.         if not self.langpacks:
  1393.             return
  1394.  
  1395.         self.do_install(self.langpacks)
  1396.  
  1397.         if len(self.languages) == 1 and self.languages[0] in ('C', 'en'):
  1398.             return # always complete enough
  1399.  
  1400.         cache = Cache()
  1401.         incomplete = False
  1402.         for pkg in self.langpacks:
  1403.             if pkg.startswith('gimp-help-'):
  1404.                 # gimp-help-common is far too big to fit on CDs, so don't
  1405.                 # worry about it.
  1406.                 continue
  1407.             cachedpkg = self.get_cache_pkg(cache, pkg)
  1408.             if cachedpkg is None or not cachedpkg.isInstalled:
  1409.                 incomplete = True
  1410.                 break
  1411.         if incomplete:
  1412.             language_support_dir = \
  1413.                 os.path.join(self.target, 'usr/share/language-support')
  1414.             update_notifier_dir = \
  1415.                 os.path.join(self.target, 'var/lib/update-notifier/user.d')
  1416.             for note in ('incomplete-language-support-gnome.note',
  1417.                          'incomplete-language-support-qt.note'):
  1418.                 notepath = os.path.join(language_support_dir, note)
  1419.                 if os.path.exists(notepath):
  1420.                     if not os.path.exists(update_notifier_dir):
  1421.                         os.makedirs(update_notifier_dir)
  1422.                     shutil.copy(notepath,
  1423.                                 os.path.join(update_notifier_dir, note))
  1424.                     break
  1425.  
  1426.  
  1427.     def select_ecryptfs(self):
  1428.         """Is ecryptfs in use by an existing user? If so, keep it installed.
  1429.  
  1430.         This duplicates code from user-setup, but necessarily so; when
  1431.         user-setup-ask runs in ubiquity, /target is not yet mounted, but we
  1432.         need to make this decision before generating the file copy blacklist
  1433.         so user-setup-apply would be too late."""
  1434.  
  1435.         home = os.path.join(self.target, 'home')
  1436.         if os.path.isdir(home):
  1437.             for homedir in os.listdir(home):
  1438.                 if os.path.isdir(os.path.join(home, homedir, '.ecryptfs')):
  1439.                     syslog.syslog('ecryptfs already in use in %s' %
  1440.                                   os.path.join(home, homedir))
  1441.                     self.record_installed(['ecryptfs-utils'])
  1442.                     break
  1443.  
  1444.  
  1445.     def configure_user(self):
  1446.         """create the user selected along the installation process
  1447.         into the installed system. Default user from live system is
  1448.         deleted and skel for this new user is copied to $HOME."""
  1449.  
  1450.         dbfilter = usersetup_apply.UserSetupApply(None)
  1451.         ret = dbfilter.run_command(auto_process=True)
  1452.         if ret != 0:
  1453.             raise InstallStepError("UserSetupApply failed with code %d" % ret)
  1454.  
  1455.     def configure_ma(self):
  1456.         """import documents, settings, and users from previous operating
  1457.         systems."""
  1458.  
  1459.         if 'UBIQUITY_MIGRATION_ASSISTANT' in os.environ:
  1460.             dbfilter = migrationassistant_apply.MigrationAssistantApply(None)
  1461.             ret = dbfilter.run_command(auto_process=True)
  1462.             if ret != 0:
  1463.                 raise InstallStepError("MigrationAssistantApply failed with code %d" % ret)
  1464.  
  1465.  
  1466.     def get_resume_partition(self):
  1467.         biggest_size = 0
  1468.         biggest_partition = None
  1469.         swaps = open('/proc/swaps')
  1470.         for line in swaps:
  1471.             words = line.split()
  1472.             if words[1] != 'partition':
  1473.                 continue
  1474.             if not os.path.exists(words[0]):
  1475.                 continue
  1476.             if words[0].startswith('/dev/ramzswap'):
  1477.                 continue
  1478.             size = int(words[2])
  1479.             if size > biggest_size:
  1480.                 biggest_size = size
  1481.                 biggest_partition = words[0]
  1482.         swaps.close()
  1483.         return biggest_partition
  1484.  
  1485.     def configure_hardware(self):
  1486.         """reconfiguring several packages which depends on the
  1487.         hardware system in which has been installed on and need some
  1488.         automatic configurations to get work."""
  1489.  
  1490.         self.chroot_setup()
  1491.         try:
  1492.             dbfilter = hw_detect.HwDetect(None, self.db)
  1493.             ret = dbfilter.run_command(auto_process=True)
  1494.             if ret != 0:
  1495.                 raise InstallStepError("HwDetect failed with code %d" % ret)
  1496.         finally:
  1497.             self.chroot_cleanup()
  1498.  
  1499.         self.db.progress('INFO', 'ubiquity/install/hardware')
  1500.  
  1501.         script = '/usr/lib/ubiquity/debian-installer-utils' \
  1502.                  '/register-module.post-base-installer'
  1503.         if 'UBIQUITY_OEM_USER_CONFIG' in os.environ:
  1504.             script += '-oem'
  1505.         misc.execute(script)
  1506.  
  1507.         resume = self.get_resume_partition()
  1508.         if resume is not None:
  1509.             resume_uuid = None
  1510.             try:
  1511.                 resume_uuid = subprocess.Popen(
  1512.                     ['block-attr', '--uuid', resume],
  1513.                     stdout=subprocess.PIPE).communicate()[0].rstrip('\n')
  1514.             except OSError:
  1515.                 pass
  1516.             if resume_uuid:
  1517.                 resume = "UUID=%s" % resume_uuid
  1518.             if os.path.exists(os.path.join(self.target,
  1519.                                            'etc/initramfs-tools/conf.d')):
  1520.                 configdir = os.path.join(self.target,
  1521.                                          'etc/initramfs-tools/conf.d')
  1522.             elif os.path.exists(os.path.join(self.target,
  1523.                                              'etc/mkinitramfs/conf.d')):
  1524.                 configdir = os.path.join(self.target,
  1525.                                          'etc/mkinitramfs/conf.d')
  1526.             else:
  1527.                 configdir = None
  1528.             if configdir is not None:
  1529.                 configfile = open(os.path.join(configdir, 'resume'), 'w')
  1530.                 print >>configfile, "RESUME=%s" % resume
  1531.                 configfile.close()
  1532.  
  1533.         try:
  1534.             os.unlink(os.path.join(self.target, 'etc/usplash.conf'))
  1535.         except OSError:
  1536.             pass
  1537.         try:
  1538.             modes = self.db.get('xserver-xorg/config/display/modes')
  1539.             self.set_debconf('xserver-xorg/config/display/modes', modes)
  1540.         except debconf.DebconfError:
  1541.             pass
  1542.  
  1543.         try:
  1544.             os.unlink(os.path.join(self.target, 'etc/popularity-contest.conf'))
  1545.         except OSError:
  1546.             pass
  1547.         try:
  1548.             participate = self.db.get('popularity-contest/participate')
  1549.             self.set_debconf('popularity-contest/participate', participate)
  1550.         except debconf.DebconfError:
  1551.             pass
  1552.  
  1553.         try:
  1554.             os.unlink(os.path.join(self.target, 'etc/papersize'))
  1555.         except OSError:
  1556.             pass
  1557.         subprocess.call(['log-output', '-t', 'ubiquity', 'chroot', self.target,
  1558.                          'ucf', '--purge', '/etc/papersize'],
  1559.                         preexec_fn=debconf_disconnect, close_fds=True)
  1560.         try:
  1561.             self.set_debconf('libpaper/defaultpaper', '')
  1562.         except debconf.DebconfError:
  1563.             pass
  1564.  
  1565.         try:
  1566.             os.unlink(os.path.join(self.target,
  1567.                       'etc/ssl/certs/ssl-cert-snakeoil.pem'))
  1568.         except OSError:
  1569.             pass
  1570.         try:
  1571.             os.unlink(os.path.join(self.target,
  1572.                       'etc/ssl/private/ssl-cert-snakeoil.key'))
  1573.         except OSError:
  1574.             pass
  1575.  
  1576.         self.chroot_setup(x11=True)
  1577.         self.chrex('dpkg-divert', '--package', 'ubiquity', '--rename',
  1578.                    '--quiet', '--add', '/usr/sbin/update-initramfs')
  1579.         try:
  1580.             os.symlink('/bin/true', os.path.join(self.target,
  1581.                                                  'usr/sbin/update-initramfs'))
  1582.         except OSError:
  1583.             pass
  1584.  
  1585.         packages = ['linux-image-' + self.kernel_version,
  1586.                     'linux-restricted-modules-' + self.kernel_version,
  1587.                     'usplash',
  1588.                     'splashy',
  1589.                     'popularity-contest',
  1590.                     'libpaper1',
  1591.                     'ssl-cert']
  1592.  
  1593.         try:
  1594.             for package in packages:
  1595.                 self.reconfigure(package)
  1596.         finally:
  1597.             try:
  1598.                 os.unlink(os.path.join(self.target,
  1599.                           'usr/sbin/update-initramfs'))
  1600.             except OSError:
  1601.                 pass
  1602.             self.chrex('dpkg-divert', '--package', 'ubiquity', '--rename',
  1603.                        '--quiet', '--remove', '/usr/sbin/update-initramfs')
  1604.             self.chrex('update-initramfs', '-c', '-k', self.kernel_version)
  1605.             self.chroot_cleanup(x11=True)
  1606.  
  1607.         # Fix up kernel symlinks now that the initrd exists. Depending on
  1608.         # the architecture, these may be in / or in /boot.
  1609.         bootdir = os.path.join(self.target, 'boot')
  1610.         if self.db.get('base-installer/kernel/linux/link_in_boot') == 'true':
  1611.             linkdir = bootdir
  1612.             linkprefix = ''
  1613.         else:
  1614.             linkdir = self.target
  1615.             linkprefix = 'boot'
  1616.  
  1617.         # Remove old symlinks. We'll set them up from scratch.
  1618.         re_symlink = re.compile('vmlinu[xz]|initrd.img$')
  1619.         for entry in os.listdir(linkdir):
  1620.             if re_symlink.match(entry) is not None:
  1621.                 filename = os.path.join(linkdir, entry)
  1622.                 if os.path.islink(filename):
  1623.                     os.unlink(filename)
  1624.         if linkdir != self.target:
  1625.             # Remove symlinks in /target too, which may have been created on
  1626.             # the live filesystem. This isn't necessary, but it may help
  1627.             # avoid confusion.
  1628.             for entry in os.listdir(self.target):
  1629.                 if re_symlink.match(entry) is not None:
  1630.                     filename = os.path.join(self.target, entry)
  1631.                     if os.path.islink(filename):
  1632.                         os.unlink(filename)
  1633.  
  1634.         # Create symlinks. Prefer our current kernel version if possible,
  1635.         # but if not (perhaps due to a customised live filesystem image),
  1636.         # it's better to create some symlinks than none at all.
  1637.         re_image = re.compile('(vmlinu[xz]|initrd.img)-')
  1638.         for entry in os.listdir(bootdir):
  1639.             match = re_image.match(entry)
  1640.             if match is not None:
  1641.                 imagetype = match.group(1)
  1642.                 linksrc = os.path.join(linkprefix, entry)
  1643.                 linkdst = os.path.join(linkdir, imagetype)
  1644.                 if os.path.exists(linkdst):
  1645.                     if entry.endswith('-' + self.kernel_version):
  1646.                         os.unlink(linkdst)
  1647.                     else:
  1648.                         continue
  1649.                 os.symlink(linksrc, linkdst)
  1650.  
  1651.  
  1652.     def get_all_interfaces(self):
  1653.         """Get all non-local network interfaces."""
  1654.         ifs = []
  1655.         ifs_file = open('/proc/net/dev')
  1656.         # eat header
  1657.         ifs_file.readline()
  1658.         ifs_file.readline()
  1659.  
  1660.         for line in ifs_file:
  1661.             name = re.match('(.*?(?::\d+)?):', line.strip()).group(1)
  1662.             if name == 'lo':
  1663.                 continue
  1664.             ifs.append(name)
  1665.  
  1666.         ifs_file.close()
  1667.         return ifs
  1668.  
  1669.  
  1670.     def configure_network(self):
  1671.         """Automatically configure the network.
  1672.  
  1673.         At present, the only thing the user gets to tweak in the UI is the
  1674.         hostname. Some other things will be copied from the live filesystem,
  1675.         so changes made there will be reflected in the installed system.
  1676.  
  1677.         Unfortunately, at present we have to duplicate a fair bit of netcfg
  1678.         here, because it's hard to drive netcfg in a way that won't try to
  1679.         bring interfaces up and down."""
  1680.  
  1681.         # TODO cjwatson 2006-03-30: just call netcfg instead of doing all
  1682.         # this; requires a netcfg binary that doesn't bring interfaces up
  1683.         # and down
  1684.  
  1685.         if self.target != '/':
  1686.             for path in ('/etc/network/interfaces', '/etc/resolv.conf'):
  1687.                 if os.path.exists(path):
  1688.                     shutil.copy2(path, os.path.join(self.target, path[1:]))
  1689.  
  1690.         try:
  1691.             hostname = self.db.get('netcfg/get_hostname')
  1692.         except debconf.DebconfError:
  1693.             hostname = ''
  1694.         try:
  1695.             domain = self.db.get('netcfg/get_domain')
  1696.         except debconf.DebconfError:
  1697.             domain = ''
  1698.         if hostname == '':
  1699.             hostname = 'ubuntu'
  1700.  
  1701.         fp = open(os.path.join(self.target, 'etc/hostname'), 'w')
  1702.         print >>fp, hostname
  1703.         fp.close()
  1704.  
  1705.         hosts = open(os.path.join(self.target, 'etc/hosts'), 'w')
  1706.         print >>hosts, "127.0.0.1\tlocalhost"
  1707.         if domain:
  1708.             print >>hosts, "127.0.1.1\t%s.%s\t%s" % (hostname, domain,
  1709.                                                      hostname)
  1710.         else:
  1711.             print >>hosts, "127.0.1.1\t%s" % hostname
  1712.         print >>hosts, textwrap.dedent("""\
  1713.  
  1714.             # The following lines are desirable for IPv6 capable hosts
  1715.             ::1     localhost ip6-localhost ip6-loopback
  1716.             fe00::0 ip6-localnet
  1717.             ff00::0 ip6-mcastprefix
  1718.             ff02::1 ip6-allnodes
  1719.             ff02::2 ip6-allrouters
  1720.             ff02::3 ip6-allhosts""")
  1721.         hosts.close()
  1722.  
  1723.         if 'UBIQUITY_OEM_USER_CONFIG' in os.environ:
  1724.             os.system("hostname %s" % hostname)
  1725.  
  1726.         persistent_net = '/etc/udev/rules.d/70-persistent-net.rules'
  1727.         if os.path.exists(persistent_net):
  1728.             if self.target != '/':
  1729.                 shutil.copy2(persistent_net,
  1730.                              os.path.join(self.target, persistent_net[1:]))
  1731.         else:
  1732.             # TODO cjwatson 2006-03-30: from <bits/ioctls.h>; ugh, but no
  1733.             # binding available
  1734.             SIOCGIFHWADDR = 0x8927
  1735.             # <net/if_arp.h>
  1736.             ARPHRD_ETHER = 1
  1737.  
  1738.             if_names = {}
  1739.             sock = socket.socket(socket.SOCK_DGRAM)
  1740.             interfaces = self.get_all_interfaces()
  1741.             for i in range(len(interfaces)):
  1742.                 if_names[interfaces[i]] = struct.unpack('H6s',
  1743.                     fcntl.ioctl(sock.fileno(), SIOCGIFHWADDR,
  1744.                                 struct.pack('256s', interfaces[i]))[16:24])
  1745.             sock.close()
  1746.  
  1747.             iftab = open(os.path.join(self.target, 'etc/iftab'), 'w')
  1748.  
  1749.             print >>iftab, textwrap.dedent("""\
  1750.                 # This file assigns persistent names to network interfaces.
  1751.                 # See iftab(5) for syntax.
  1752.                 """)
  1753.  
  1754.             for i in range(len(interfaces)):
  1755.                 dup = False
  1756.                 with_arp = False
  1757.  
  1758.                 if_name = if_names[interfaces[i]]
  1759.                 if if_name is None or if_name[0] != ARPHRD_ETHER:
  1760.                     continue
  1761.  
  1762.                 for j in range(len(interfaces)):
  1763.                     if i == j or if_names[interfaces[j]] is None:
  1764.                         continue
  1765.                     if if_name[1] != if_names[interfaces[j]][1]:
  1766.                         continue
  1767.  
  1768.                     if if_names[interfaces[j]][0] == ARPHRD_ETHER:
  1769.                         dup = True
  1770.  
  1771.                 if dup:
  1772.                     continue
  1773.  
  1774.                 line = (interfaces[i] + " mac " +
  1775.                         ':'.join(['%02x' % ord(if_name[1][c])
  1776.                                   for c in range(6)]))
  1777.                 line += " arp %d" % if_name[0]
  1778.                 print >>iftab, line
  1779.  
  1780.             iftab.close()
  1781.  
  1782.  
  1783.     def configure_bootloader(self):
  1784.         """configuring and installing boot loader into installed
  1785.         hardware system."""
  1786.         install_bootloader = self.db.get('ubiquity/install_bootloader')
  1787.         if install_bootloader == "true":
  1788.             misc.execute('mount', '--bind', '/proc', self.target + '/proc')
  1789.             misc.execute('mount', '--bind', '/sys', self.target + '/sys')
  1790.             misc.execute('mount', '--bind', '/dev', self.target + '/dev')
  1791.  
  1792.             arch, subarch = self.archdetect()
  1793.  
  1794.             try:
  1795.                 if arch in ('amd64', 'i386', 'lpia'):
  1796.                     from ubiquity.components import grubinstaller
  1797.                     dbfilter = grubinstaller.GrubInstaller(None)
  1798.                     ret = dbfilter.run_command(auto_process=True)
  1799.                     if ret != 0:
  1800.                         raise InstallStepError(
  1801.                             "GrubInstaller failed with code %d" % ret)
  1802.                 elif (arch == 'armel' and
  1803.                       subarch in ('dove', 'imx51', 'iop32x', 'ixp4xx', 'orion5x')):
  1804.                     from ubiquity.components import flash_kernel
  1805.                     dbfilter = flash_kernel.FlashKernel(None)
  1806.                     ret = dbfilter.run_command(auto_process=True)
  1807.                     if ret != 0:
  1808.                         raise InstallStepError(
  1809.                             "FlashKernel failed with code %d" % ret)
  1810.                 elif arch == 'powerpc' and subarch == 'ps3':
  1811.                     from ubiquity.components import kbootinstaller
  1812.                     dbfilter = kbootinstaller.KbootInstaller(None)
  1813.                     ret = dbfilter.run_command(auto_process=True)
  1814.                     if ret != 0:
  1815.                         raise InstallStepError(
  1816.                             "KbootInstaller failed with code %d" % ret)
  1817.                 elif arch == 'powerpc':
  1818.                     from ubiquity.components import yabootinstaller
  1819.                     dbfilter = yabootinstaller.YabootInstaller(None)
  1820.                     ret = dbfilter.run_command(auto_process=True)
  1821.                     if ret != 0:
  1822.                         raise InstallStepError(
  1823.                             "YabootInstaller failed with code %d" % ret)
  1824.                 else:
  1825.                     raise InstallStepError("No bootloader installer found")
  1826.             except ImportError:
  1827.                 raise InstallStepError("No bootloader installer found")
  1828.  
  1829.             misc.execute('umount', '-f', self.target + '/proc')
  1830.             misc.execute('umount', '-f', self.target + '/sys')
  1831.             misc.execute('umount', '-f', self.target + '/dev')
  1832.  
  1833.  
  1834.     def broken_packages(self, cache):
  1835.         expect_count = cache._depcache.BrokenCount
  1836.         count = 0
  1837.         brokenpkgs = set()
  1838.         for pkg in cache.keys():
  1839.             try:
  1840.                 if cache._depcache.IsInstBroken(cache._cache[pkg]):
  1841.                     brokenpkgs.add(pkg)
  1842.                     count += 1
  1843.             except KeyError:
  1844.                 # Apparently sometimes the cache goes a bit bonkers ...
  1845.                 continue
  1846.             if count >= expect_count:
  1847.                 break
  1848.         return brokenpkgs
  1849.  
  1850.     def do_install(self, to_install):
  1851.         if self.langpacks:
  1852.             self.db.progress('START', 0, 10, 'ubiquity/langpacks/title')
  1853.         else:
  1854.             self.db.progress('START', 0, 10, 'ubiquity/install/title')
  1855.         self.db.progress('INFO', 'ubiquity/install/find_installables')
  1856.  
  1857.         self.db.progress('REGION', 0, 1)
  1858.         fetchprogress = DebconfFetchProgress(
  1859.             self.db, 'ubiquity/install/title',
  1860.             'ubiquity/install/apt_indices_starting',
  1861.             'ubiquity/install/apt_indices')
  1862.         cache = Cache()
  1863.  
  1864.         if cache._depcache.BrokenCount > 0:
  1865.             syslog.syslog(
  1866.                 'not installing additional packages, since there are broken '
  1867.                 'packages: %s' % ', '.join(self.broken_packages(cache)))
  1868.             self.db.progress('STOP')
  1869.             return
  1870.  
  1871.         for pkg in to_install:
  1872.             self.mark_install(cache, pkg)
  1873.  
  1874.         self.db.progress('SET', 1)
  1875.         self.db.progress('REGION', 1, 10)
  1876.         if self.langpacks:
  1877.             fetchprogress = DebconfFetchProgress(
  1878.                 self.db, 'ubiquity/langpacks/title', None,
  1879.                 'ubiquity/langpacks/packages')
  1880.             installprogress = DebconfInstallProgress(
  1881.                 self.db, 'ubiquity/langpacks/title',
  1882.                 'ubiquity/install/apt_info')
  1883.         else:
  1884.             fetchprogress = DebconfFetchProgress(
  1885.                 self.db, 'ubiquity/install/title', None,
  1886.                 'ubiquity/install/fetch_remove')
  1887.             installprogress = DebconfInstallProgress(
  1888.                 self.db, 'ubiquity/install/title',
  1889.                 'ubiquity/install/apt_info',
  1890.                 'ubiquity/install/apt_error_install')
  1891.         self.chroot_setup()
  1892.         commit_error = None
  1893.         try:
  1894.             try:
  1895.                 if not cache.commit(fetchprogress, installprogress):
  1896.                     fetchprogress.stop()
  1897.                     installprogress.finishUpdate()
  1898.                     self.db.progress('STOP')
  1899.                     return
  1900.             except IOError, e:
  1901.                 for line in str(e).split('\n'):
  1902.                     syslog.syslog(syslog.LOG_ERR, line)
  1903.                 fetchprogress.stop()
  1904.                 installprogress.finishUpdate()
  1905.                 self.db.progress('STOP')
  1906.                 return
  1907.             except SystemError, e:
  1908.                 for line in str(e).split('\n'):
  1909.                     syslog.syslog(syslog.LOG_ERR, line)
  1910.                 commit_error = str(e)
  1911.         finally:
  1912.             self.chroot_cleanup()
  1913.         self.db.progress('SET', 10)
  1914.  
  1915.         cache.open(None)
  1916.         if commit_error or cache._depcache.BrokenCount > 0:
  1917.             if commit_error is None:
  1918.                 commit_error = ''
  1919.             brokenpkgs = self.broken_packages(cache)
  1920.             syslog.syslog('broken packages after installation: '
  1921.                           '%s' % ', '.join(brokenpkgs))
  1922.             self.db.subst('ubiquity/install/broken_install', 'ERROR',
  1923.                           commit_error)
  1924.             self.db.subst('ubiquity/install/broken_install', 'PACKAGES',
  1925.                           ', '.join(brokenpkgs))
  1926.             self.db.input('critical', 'ubiquity/install/broken_install')
  1927.             self.db.go()
  1928.  
  1929.         self.db.progress('STOP')
  1930.  
  1931.  
  1932.     def expand_dependencies_simple(self, cache, keep, to_remove,
  1933.                                    recommends=True):
  1934.         """Return the list of packages in to_remove that clearly cannot be
  1935.         removed if we want to keep the set of packages in keep. Except in
  1936.         the case of Recommends, this is not required for correctness (we
  1937.         could just let apt figure it out), but it allows us to ask apt fewer
  1938.         separate questions, and so is faster."""
  1939.  
  1940.         keys = ['Pre-Depends', 'Depends']
  1941.         if recommends:
  1942.             keys.append('Recommends')
  1943.  
  1944.         to_scan = set(keep)
  1945.         to_scan_next = set()
  1946.         expanded = set(keep)
  1947.         while to_scan:
  1948.             for pkg in to_scan:
  1949.                 cachedpkg = self.get_cache_pkg(cache, pkg)
  1950.                 if cachedpkg is None:
  1951.                     continue
  1952.                 ver = cachedpkg._pkg.CurrentVer
  1953.                 if ver is None:
  1954.                     continue
  1955.                 for key in keys:
  1956.                     if key in ver.DependsList:
  1957.                         for dep_or in ver.DependsList[key]:
  1958.                             # Keep the first element of a disjunction that's
  1959.                             # installed; this mirrors what 'apt-get install'
  1960.                             # would do if you were installing the package
  1961.                             # from scratch. This doesn't handle versioned
  1962.                             # dependencies, but that's largely OK since apt
  1963.                             # will spot those later; the only case I can
  1964.                             # think of where this might have trouble is
  1965.                             # "Recommends: foo (>= 2) | bar".
  1966.                             for dep in dep_or:
  1967.                                 depname = dep.TargetPkg.Name
  1968.                                 cacheddep = self.get_cache_pkg(cache, depname)
  1969.                                 if cacheddep is None:
  1970.                                     continue
  1971.                                 if cacheddep._pkg.CurrentVer is not None:
  1972.                                     break
  1973.                             else:
  1974.                                 continue
  1975.                             if depname in expanded or depname not in to_remove:
  1976.                                 continue
  1977.                             expanded.add(depname)
  1978.                             to_scan_next.add(depname)
  1979.             to_scan = to_scan_next
  1980.             to_scan_next = set()
  1981.  
  1982.         return expanded
  1983.  
  1984.  
  1985.     def get_remove_list(self, cache, to_remove, recursive=False):
  1986.         to_remove = set(to_remove)
  1987.         all_removed = set()
  1988.         while True:
  1989.             removed = set()
  1990.             for pkg in to_remove:
  1991.                 cachedpkg = self.get_cache_pkg(cache, pkg)
  1992.                 if cachedpkg is not None and cachedpkg.isInstalled:
  1993.                     apt_error = False
  1994.                     try:
  1995.                         cachedpkg.markDelete(autoFix=False, purge=True)
  1996.                     except SystemError:
  1997.                         apt_error = True
  1998.                     if apt_error:
  1999.                         cachedpkg.markKeep()
  2000.                     elif cache._depcache.BrokenCount > 0:
  2001.                         # If we're recursively removing packages, or if all
  2002.                         # of the broken packages are in the set of packages
  2003.                         # to remove anyway, then go ahead and try to remove
  2004.                         # them too.
  2005.                         brokenpkgs = self.broken_packages(cache)
  2006.                         broken_removed = set()
  2007.                         while brokenpkgs and (recursive or
  2008.                                               brokenpkgs <= to_remove):
  2009.                             broken_removed_inner = set()
  2010.                             for pkg2 in brokenpkgs:
  2011.                                 cachedpkg2 = self.get_cache_pkg(cache, pkg2)
  2012.                                 if cachedpkg2 is not None:
  2013.                                     broken_removed_inner.add(pkg2)
  2014.                                     try:
  2015.                                         cachedpkg2.markDelete(autoFix=False,
  2016.                                                               purge=True)
  2017.                                     except SystemError:
  2018.                                         apt_error = True
  2019.                                         break
  2020.                             broken_removed |= broken_removed_inner
  2021.                             if apt_error or not broken_removed_inner:
  2022.                                 break
  2023.                             brokenpkgs = self.broken_packages(cache)
  2024.                         if apt_error or cache._depcache.BrokenCount > 0:
  2025.                             # That didn't work. Revert all the removals we
  2026.                             # just tried.
  2027.                             for pkg2 in broken_removed:
  2028.                                 self.get_cache_pkg(cache, pkg2).markKeep()
  2029.                             cachedpkg.markKeep()
  2030.                         else:
  2031.                             removed.add(pkg)
  2032.                             removed |= broken_removed
  2033.                     else:
  2034.                         removed.add(pkg)
  2035.                     assert cache._depcache.BrokenCount == 0
  2036.             if not removed:
  2037.                 break
  2038.             to_remove -= removed
  2039.             all_removed |= removed
  2040.         return all_removed
  2041.  
  2042.  
  2043.     def do_remove(self, to_remove, recursive=False):
  2044.         self.db.progress('START', 0, 5, 'ubiquity/install/title')
  2045.         self.db.progress('INFO', 'ubiquity/install/find_removables')
  2046.  
  2047.         fetchprogress = DebconfFetchProgress(
  2048.             self.db, 'ubiquity/install/title',
  2049.             'ubiquity/install/apt_indices_starting',
  2050.             'ubiquity/install/apt_indices')
  2051.         cache = Cache()
  2052.  
  2053.         if cache._depcache.BrokenCount > 0:
  2054.             syslog.syslog(
  2055.                 'not processing removals, since there are broken packages: '
  2056.                 '%s' % ', '.join(self.broken_packages(cache)))
  2057.             self.db.progress('STOP')
  2058.             return
  2059.  
  2060.         self.get_remove_list(cache, to_remove, recursive)
  2061.  
  2062.         self.db.progress('SET', 1)
  2063.         self.db.progress('REGION', 1, 5)
  2064.         fetchprogress = DebconfFetchProgress(
  2065.             self.db, 'ubiquity/install/title', None,
  2066.             'ubiquity/install/fetch_remove')
  2067.         installprogress = DebconfInstallProgress(
  2068.             self.db, 'ubiquity/install/title', 'ubiquity/install/apt_info',
  2069.             'ubiquity/install/apt_error_remove')
  2070.         self.chroot_setup()
  2071.         commit_error = None
  2072.         try:
  2073.             try:
  2074.                 if not cache.commit(fetchprogress, installprogress):
  2075.                     fetchprogress.stop()
  2076.                     installprogress.finishUpdate()
  2077.                     self.db.progress('STOP')
  2078.                     return
  2079.             except SystemError, e:
  2080.                 for line in str(e).split('\n'):
  2081.                     syslog.syslog(syslog.LOG_ERR, line)
  2082.                 commit_error = str(e)
  2083.         finally:
  2084.             self.chroot_cleanup()
  2085.         self.db.progress('SET', 5)
  2086.  
  2087.         cache.open(None)
  2088.         if commit_error or cache._depcache.BrokenCount > 0:
  2089.             if commit_error is None:
  2090.                 commit_error = ''
  2091.             brokenpkgs = self.broken_packages(cache)
  2092.             syslog.syslog('broken packages after removal: '
  2093.                           '%s' % ', '.join(brokenpkgs))
  2094.             self.db.subst('ubiquity/install/broken_remove', 'ERROR',
  2095.                           commit_error)
  2096.             self.db.subst('ubiquity/install/broken_remove', 'PACKAGES',
  2097.                           ', '.join(brokenpkgs))
  2098.             self.db.input('critical', 'ubiquity/install/broken_remove')
  2099.             self.db.go()
  2100.  
  2101.         self.db.progress('STOP')
  2102.  
  2103.     def traverse_for_kernel(self, cache, pkg):
  2104.         kern = self.get_cache_pkg(cache, pkg)
  2105.         if kern is None:
  2106.             return None
  2107.         pkc = cache._depcache.GetCandidateVer(kern._pkg)
  2108.         if pkc.DependsList.has_key('Depends'):
  2109.             dependencies = pkc.DependsList['Depends']
  2110.         else:
  2111.             # Didn't find.
  2112.             return None
  2113.         for dep in dependencies:
  2114.             name = dep[0].TargetPkg.Name
  2115.             if name.startswith('linux-image-2.'):
  2116.                 return name
  2117.             elif name.startswith('linux-'):
  2118.                 return self.traverse_for_kernel(cache, name)
  2119.  
  2120.     def remove_unusable_kernels(self):
  2121.         """Remove unusable kernels; keeping them may cause us to be unable
  2122.         to boot."""
  2123.  
  2124.         if 'UBIQUITY_OEM_USER_CONFIG' in os.environ:
  2125.             return
  2126.  
  2127.         self.db.progress('START', 0, 5, 'ubiquity/install/title')
  2128.  
  2129.         self.db.progress('INFO', 'ubiquity/install/find_removables')
  2130.  
  2131.         # Check for kernel packages to remove.
  2132.         dbfilter = check_kernels.CheckKernels(None)
  2133.         dbfilter.run_command(auto_process=True)
  2134.  
  2135.         install_kernels = set()
  2136.         new_kernel_pkg = None
  2137.         new_kernel_version = None
  2138.         if os.path.exists("/var/lib/ubiquity/install-kernels"):
  2139.             install_kernels_file = open("/var/lib/ubiquity/install-kernels")
  2140.             for line in install_kernels_file:
  2141.                 kernel = line.strip()
  2142.                 install_kernels.add(kernel)
  2143.                 # If we decided to actively install a particular kernel like
  2144.                 # this, it's probably because we prefer it to the default
  2145.                 # one, so we'd better update kernel_version to match.
  2146.                 if kernel.startswith('linux-image-2.'):
  2147.                     new_kernel_pkg = kernel
  2148.                     new_kernel_version = kernel[12:]
  2149.                 elif kernel.startswith('linux-generic-'):
  2150.                     # Traverse dependencies to find the real kernel image.
  2151.                     cache = Cache()
  2152.                     kernel = self.traverse_for_kernel(cache, kernel)
  2153.                     if kernel:
  2154.                         new_kernel_pkg = kernel
  2155.                         new_kernel_version = kernel[12:]
  2156.             install_kernels_file.close()
  2157.  
  2158.         remove_kernels = set()
  2159.         if os.path.exists("/var/lib/ubiquity/remove-kernels"):
  2160.             remove_kernels_file = open("/var/lib/ubiquity/remove-kernels")
  2161.             for line in remove_kernels_file:
  2162.                 remove_kernels.add(line.strip())
  2163.             remove_kernels_file.close()
  2164.  
  2165.         if len(install_kernels) == 0 and len(remove_kernels) == 0:
  2166.             self.db.progress('STOP')
  2167.             return
  2168.  
  2169.         # TODO cjwatson 2009-10-19: These regions are rather crude and
  2170.         # should be improved.
  2171.         self.db.progress('SET', 1)
  2172.         self.db.progress('REGION', 1, 2)
  2173.         if install_kernels:
  2174.             self.do_install(install_kernels)
  2175.             if new_kernel_pkg:
  2176.                 cache = Cache()
  2177.                 cached_pkg = self.get_cache_pkg(cache, new_kernel_pkg)
  2178.                 if cached_pkg is not None and cached_pkg.isInstalled:
  2179.                     self.kernel_version = new_kernel_version
  2180.                 else:
  2181.                     remove_kernels = []
  2182.                 del cache
  2183.             else:
  2184.                 remove_kernels = []
  2185.  
  2186.         self.db.progress('SET', 2)
  2187.         self.db.progress('REGION', 2, 5)
  2188.         try:
  2189.             if remove_kernels:
  2190.                 self.do_remove(remove_kernels, recursive=True)
  2191.         except:
  2192.             self.db.progress('STOP')
  2193.             raise
  2194.         self.db.progress('SET', 5)
  2195.         self.db.progress('STOP')
  2196.  
  2197.  
  2198.     def install_extras(self):
  2199.         """Try to install additional packages requested by installer
  2200.         components."""
  2201.  
  2202.         if 'UBIQUITY_OEM_USER_CONFIG' in os.environ:
  2203.             return
  2204.  
  2205.         # We only ever install these packages from the CD.
  2206.         sources_list = os.path.join(self.target, 'etc/apt/sources.list')
  2207.         os.rename(sources_list, "%s.apt-setup" % sources_list)
  2208.         old_sources = open("%s.apt-setup" % sources_list)
  2209.         new_sources = open(sources_list, 'w')
  2210.         found_cdrom = False
  2211.         for line in old_sources:
  2212.             if 'cdrom:' in line:
  2213.                 print >>new_sources, line,
  2214.                 found_cdrom = True
  2215.         new_sources.close()
  2216.         old_sources.close()
  2217.         if not found_cdrom:
  2218.             os.rename("%s.apt-setup" % sources_list, sources_list)
  2219.  
  2220.         self.do_install(self.query_recorded_installed())
  2221.  
  2222.         if found_cdrom:
  2223.             os.rename("%s.apt-setup" % sources_list, sources_list)
  2224.  
  2225.         # TODO cjwatson 2007-08-09: python reimplementation of
  2226.         # oem-config/finish-install.d/07oem-config-user. This really needs
  2227.         # to die in a great big chemical fire and call the same shell script
  2228.         # instead.
  2229.         try:
  2230.             if self.db.get('oem-config/enable') == 'true':
  2231.                 if os.path.isdir(os.path.join(self.target, 'home/oem')):
  2232.                     open(os.path.join(self.target, 'home/oem/.hwdb'),
  2233.                          'w').close()
  2234.  
  2235.                     for desktop_file in (
  2236.                         'usr/share/applications/oem-config-prepare-gtk.desktop',
  2237.                         'usr/share/applications/kde/oem-config-prepare-kde.desktop'):
  2238.                         if os.path.exists(os.path.join(self.target,
  2239.                                                        desktop_file)):
  2240.                             desktop_base = os.path.basename(desktop_file)
  2241.                             self.chrex('install', '-d',
  2242.                                        '-o', 'oem', '-g', 'oem',
  2243.                                        '/home/oem/Desktop')
  2244.                             self.chrex('install', '-o', 'oem', '-g', 'oem',
  2245.                                        '/%s' % desktop_file,
  2246.                                        '/home/oem/Desktop/%s' % desktop_base)
  2247.                             break
  2248.  
  2249.         # Carry the locale setting over to the installed system.
  2250.         # This mimics the behavior in 01oem-config-udeb.
  2251.                 di_locale = self.db.get('debian-installer/locale')
  2252.                 if di_locale:
  2253.                     self.set_debconf('debian-installer/locale', di_locale)
  2254.         except debconf.DebconfError:
  2255.             pass
  2256.  
  2257.  
  2258.     def remove_extras(self):
  2259.         """Try to remove packages that are needed on the live CD but not on
  2260.         the installed system."""
  2261.  
  2262.         if 'UBIQUITY_OEM_USER_CONFIG' in os.environ:
  2263.             return
  2264.  
  2265.         # Looking through files for packages to remove is pretty quick, so
  2266.         # don't bother with a progress bar for that.
  2267.  
  2268.         # Check for packages specific to the live CD.
  2269.         manifest_desktop = os.path.join(self.casper_path,
  2270.                                         'filesystem.manifest-desktop')
  2271.         manifest = os.path.join(self.casper_path, 'filesystem.manifest')
  2272.         if (os.path.exists(manifest_desktop) and
  2273.             os.path.exists(manifest)):
  2274.             desktop_packages = set()
  2275.             manifest_file = open(manifest_desktop)
  2276.             for line in manifest_file:
  2277.                 if line.strip() != '' and not line.startswith('#'):
  2278.                     desktop_packages.add(line.split()[0])
  2279.             manifest_file.close()
  2280.             live_packages = set()
  2281.             manifest_file = open(manifest)
  2282.             for line in manifest_file:
  2283.                 if line.strip() != '' and not line.startswith('#'):
  2284.                     live_packages.add(line.split()[0])
  2285.             manifest_file.close()
  2286.             difference = live_packages - desktop_packages
  2287.         else:
  2288.             difference = set()
  2289.  
  2290.         # Keep packages we explicitly installed.
  2291.         keep = self.query_recorded_installed()
  2292.  
  2293.         arch, subarch = self.archdetect()
  2294.  
  2295.         if arch in ('amd64', 'i386', 'lpia'):
  2296.             if 'grub' not in keep:
  2297.                 difference.add('grub')
  2298.             if 'grub-pc' not in keep:
  2299.                 difference.add('grub-pc')
  2300.             if 'lilo' not in keep:
  2301.                 difference.add('lilo')
  2302.  
  2303.         cache = Cache()
  2304.         difference -= self.expand_dependencies_simple(cache, keep, difference)
  2305.         del cache
  2306.  
  2307.         if len(difference) == 0:
  2308.             return
  2309.  
  2310.         use_restricted = True
  2311.         try:
  2312.             if self.db.get('apt-setup/restricted') == 'false':
  2313.                 use_restricted = False
  2314.         except debconf.DebconfError:
  2315.             pass
  2316.         if not use_restricted:
  2317.             cache = self.restricted_cache
  2318.             for pkg in cache.keys():
  2319.                 if (cache[pkg].isInstalled and
  2320.                     cache[pkg].section.startswith('restricted/')):
  2321.                     difference.add(pkg)
  2322.             del cache
  2323.  
  2324.         # Don't worry about failures removing packages; it will be easier
  2325.         # for the user to sort them out with a graphical package manager (or
  2326.         # whatever) after installation than it will be to try to deal with
  2327.         # them automatically here.
  2328.         self.do_remove(difference)
  2329.  
  2330.     def remove_broken_cdrom(self):
  2331.         if 'UBIQUITY_OEM_USER_CONFIG' in os.environ:
  2332.             return
  2333.         fstab = os.path.join(self.target, 'etc/fstab')
  2334.         ret = []
  2335.         try:
  2336.             fp = open(fstab)
  2337.             for line in fp:
  2338.                 l = line.split()
  2339.                 if len(l) > 2:
  2340.                     if l[1].startswith('/cdrom') or l[1].startswith('/media/cdrom'):
  2341.                         try:
  2342.                             fstype = subprocess.Popen(
  2343.                                 ['block-attr', '--type', l[0]],
  2344.                                 stdout=subprocess.PIPE).communicate()[0].rstrip('\n')
  2345.                             if fstype != 'iso9660' and fstype != 'udf':
  2346.                                 continue
  2347.                         except OSError:
  2348.                             pass
  2349.                 ret.append(line)
  2350.             fp.close()
  2351.             fp = open(fstab, 'w')
  2352.             fp.writelines(ret)
  2353.         except Exception, e:
  2354.             syslog.syslog(syslog.LOG_ERR, 'Exception during installation:')
  2355.             syslog.syslog(syslog.LOG_ERR,
  2356.                 'Unable to process /etc/fstab: ' + str(e))
  2357.         finally:
  2358.             if fp:
  2359.                 fp.close()
  2360.             
  2361.     def cleanup(self):
  2362.         """Miscellaneous cleanup tasks."""
  2363.  
  2364.         misc.execute('umount', os.path.join(self.target, 'cdrom'))
  2365.  
  2366.         env = dict(os.environ)
  2367.         env['OVERRIDE_BASE_INSTALLABLE'] = '1'
  2368.         subprocess.call(['/usr/lib/ubiquity/apt-setup/finish-install'],
  2369.                         env=env)
  2370.  
  2371.         for apt_conf in ('00NoMountCDROM', '00IgnoreTimeConflict',
  2372.                          '00AllowUnauthenticated'):
  2373.             try:
  2374.                 os.unlink(os.path.join(
  2375.                     self.target, 'etc/apt/apt.conf.d', apt_conf))
  2376.             except:
  2377.                 pass
  2378.  
  2379.         if self.source == '/var/lib/ubiquity/source':
  2380.             self.umount_source()
  2381.  
  2382.  
  2383.     def chrex(self, *args):
  2384.         """executes commands on chroot system (provided by *args)."""
  2385.         return misc.execute('chroot', self.target, *args)
  2386.  
  2387.  
  2388.     def copy_debconf(self, package):
  2389.         """setting debconf database into installed system."""
  2390.  
  2391.         # TODO cjwatson 2006-02-25: unusable here now because we have a
  2392.         # running debconf frontend that's locked the database; fortunately
  2393.         # this isn't critical. We still need to think about how to handle
  2394.         # preseeding in general, though.
  2395.         targetdb = os.path.join(self.target, 'var/cache/debconf/config.dat')
  2396.  
  2397.         misc.execute('debconf-copydb', 'configdb', 'targetdb', '-p',
  2398.                      '^%s/' % package, '--config=Name:targetdb',
  2399.                      '--config=Driver:File','--config=Filename:' + targetdb)
  2400.  
  2401.  
  2402.     def set_debconf(self, question, value):
  2403.         try:
  2404.             if 'UBIQUITY_OEM_USER_CONFIG' in os.environ:
  2405.                 dccomm = None
  2406.                 dc = self.db
  2407.             else:
  2408.                 dccomm = subprocess.Popen(['log-output', '-t', 'ubiquity',
  2409.                                            '--pass-stdout',
  2410.                                            'chroot', self.target,
  2411.                                            'debconf-communicate',
  2412.                                            '-fnoninteractive', 'ubiquity'],
  2413.                                           stdin=subprocess.PIPE,
  2414.                                           stdout=subprocess.PIPE, close_fds=True)
  2415.                 dc = debconf.Debconf(read=dccomm.stdout, write=dccomm.stdin)
  2416.             dc.set(question, value)
  2417.             dc.fset(question, 'seen', 'true')
  2418.         finally:
  2419.             if dccomm:
  2420.                 dccomm.stdin.close()
  2421.                 dccomm.wait()
  2422.  
  2423.  
  2424.     def reconfigure_preexec(self):
  2425.         debconf_disconnect()
  2426.         os.environ['XAUTHORITY'] = '/root/.Xauthority'
  2427.  
  2428.     def reconfigure(self, package):
  2429.         """executes a dpkg-reconfigure into installed system to each
  2430.         package which provided by args."""
  2431.         subprocess.call(['log-output', '-t', 'ubiquity', 'chroot', self.target,
  2432.                          'dpkg-reconfigure', '-fnoninteractive', package],
  2433.                         preexec_fn=self.reconfigure_preexec, close_fds=True)
  2434.  
  2435.  
  2436. if __name__ == '__main__':
  2437.     if not os.path.exists('/var/lib/ubiquity'):
  2438.         os.makedirs('/var/lib/ubiquity')
  2439.     if os.path.exists('/var/lib/ubiquity/install.trace'):
  2440.         os.unlink('/var/lib/ubiquity/install.trace')
  2441.  
  2442.     install = Install()
  2443.     sys.excepthook = install.excepthook
  2444.     install.run()
  2445.     sys.exit(0)
  2446.  
  2447. # vim:ai:et:sts=4:tw=80:sw=4:
  2448.